2012-06-06 4 views
3

이것은 내 첫 번째 게시물입니다. XNA를 사용하여 Visual Studio 2010에서 게임을 만들고 있는데, 거대한 메모리 누수가 발생했습니다. 내 게임은 17k 램을 사용하여 시작하고 10 분 후에는 65k 개까지 진행됩니다. 일부 메모리 프로파일 러를 돌렸고 String 객체의 새 인스턴스가 생성되고 있지만 라이브는 아닙니다. String의 실제 인스턴스 양은 전혀 변경되지 않았습니다. 또한 Char [] (인스턴스에서 기대하는), Object [] 및 StringBuilder 인스턴스를 만듭니다. 내 게임은 꽤 새롭지 만 여기에 게시 할 코드가 너무 많습니다. 나는 살아 있지 않은 경우를 제거하는 방법을 모릅니다. 제발 도와주세요!C#/XNA Giant Memory Leak

+5

나는 65K는 "거대한 메모리 누수"이라고 생각 모르겠어요. 30 년 전부터 개인용 컴퓨터에 거의 맞출 수있었습니다. 그것이 650MB까지 불고 있다면 나는 조금 걱정할지도 모른다. –

+0

나는 당신이 심지어 17K 호스트 프로세스 크기로 내려갈 수 있다고 생각하지 않기 때문에 65,000K까지 의미해야한다고 생각합니다. 그럼에도 불구하고 65M은 현대 표준으로는 그다지 크지 않습니다. –

+0

좋아요, 괜찮 으면 그걸 미끄러 뜨리겠습니다. 때때로 재설정됩니다. 정말 고마워! 그리고 예, 나는 65,000k를 의미했습니다. – Kirbyfanner

답변

2

C#에서는 메모리 누수가 없습니다 (또는 잘 모르지만, 그들은 매우 어려움). 당신이 겪고있는 것은 정상입니다. 가비지 수집기는 메모리를 수집해야하는 것처럼 "느끼지"않으므로 그렇게하지 않습니다. 메모리가 부족하면 가비지 수집이 발생합니다. 인 경우 인 경우 string에 대한 불필요한 참조를 보관하지 않아도됩니다.

강제로 GC주기를 사용하려면 GC.Collect()을 사용하십시오.

+6

게임에서'GC.Collect'를 사용하는 것은 좋은 생각이 아닙니다. 게임 루프 내에서 객체 풀링을 사용하고 동적 인 할당을 피하는 것이 좋습니다. –

+0

XNA의 일부는 관리 코드가 아니므로 GC가 작동하지 않습니다. 이렇게 : http://stackoverflow.com/questions/3364481/outofmemory-exception-when-drawing-cube –

5

교육받은 추측을 넘어서는 충분한 정보를 게시하지 않았습니다.

당신이 당신의 그리기 방법이 같은 일을하는 경우 :

spriteBatch.DrawString(font, "Score: " + score, location, Color.Black); 
spriteBatch.DrawString(font, "Something else: " + foo, overHere, Color.Black); 
spriteBatch.DrawString(font, "And also: " + bar, overThere, Color.Black); 
다음

이러한 호출은 각각 허리 뒤에 새로운 stringStringBuilder 객체가 실행할 때마다 생성됩니다 여기 내 추측이다. 그들은 귀하의 Draw 방법에 있기 때문에 각각 초당 60 회씩 실행됩니다. 그것은 할당되는 많은 임시 객체입니다!

이 경우를 확인하려면 CLR 프로파일 러를 사용하십시오. 이미 해본 것처럼 들리는군요.

이것은 실제로 "누출"이 아닙니다. 가비지 수집기가 결국에는이를 정리합니다.이 할당 패턴은 게임에서 바람직하지 않습니다. 게임에서 가비지 수집을 다루는 두 가지 방법에 대한 내용은 this blog post을 참조하십시오. 방법 1은 일반적으로 더 쉽고 더 나은 결과를 제공하므로 여기에서 설명하겠습니다.

이 시점에서 PC의 GC가 충분히 빠르므로 과 같은 할당이 실제로과 관련이 없다고 언급 할 가치가 있습니다. GC는 매우 적은 오버 헤드로 작은 객체 (예 : 임시 문자열)를 정리합니다.

반면 Xbox 360에서는 이와 같이 정기적으로 소량의 쓰레기를 생성해도 심각한 성능 문제가 발생할 수 있습니다. (WP7에 대해서는 잘 모르겠지만, 개인적으로 Xbox처럼 취급 할 것입니다.)

어떻게 수정합니까?

대답은 간단하다 : DrawStringstring 대신에 StringBuilder의 인스턴스를 받아 들일 것입니다. StringBuilder의 인스턴스 하나를 만든 다음 사용자 지정 문자열을 구성해야 할 때마다 다시 사용하십시오.

숫자 또는 다른 개체를 암시 적으로 또는 ToString() 메서드로 문자열로 변환하면 할당이 발생합니다. 따라서 할당을하지 않고 StringBuilder에 추가 할 사용자 지정 코드를 작성해야 할 수 있습니다.여기

은 할당하지 않고 내가 문자열로 정수를 추가하기위한, 확장 메서드의 형태로 사용 하나입니다

public static class StringBuilderExtensions 
{ 
    // 11 characters will fit -4294967296 
    static char[] numberBuffer = new char[11]; 

    /// <summary>Append an integer without generating any garbage.</summary> 
    public static StringBuilder AppendNumber(this StringBuilder sb, Int32 number) 
    { 
     bool negative = (number < 0); 
     if(negative) 
      number = -number; 

     int i = numberBuffer.Length; 
     do 
     { 
      numberBuffer[--i] = (char)('0' + (number % 10)); 
      number /= 10; 
     } 
     while(number > 0); 

     if(negative) 
      numberBuffer[--i] = '-'; 

     sb.Append(numberBuffer, i, numberBuffer.Length - i); 

     return sb; 
    } 
} 
+0

기술적으로 numberBuffer가 필요하지 않습니다. (캐시 히트를 줄이기 위해 더 빨리) 한 번에 1 개의 char을 추가하십시오. 크기 조정이 걱정되면 sb.EnsureCapacity (sb.Length + 16)를 사용하여 캐시 라인 정렬을 유지하십시오 (16). – GGulati

+0

아, 와우,이게 너무 의미가 있습니다. 내가 할 수있는 한 빨리 이걸 시도 할께. 고마워. – Kirbyfanner

+0

@GGulati 캐릭터에 'Append'를 호출하는 것이 버퍼에서 문자열을 만들고 그곳으로 떨어 뜨리는 것보다 약간 더 낫습니다 ** 알고리즘은 오른쪽에서 왼쪽으로 문자열을 구성합니다 **. 운동으로 왼쪽에서 오른쪽으로 알고리즘을 떠날 것입니다;) 더 나아가 'EnsureCapacity'에 대해 걱정할 필요가 없습니다. 요점은 버퍼를 재사용하는 것입니다. 따라서 용량을 늘리는 것은 한 번 발생합니다. 그리고 CPU 시간은 아주 작은 차이입니다. 이는 특정 GC 최적화입니다. 상단에 불필요한 최적화를 추가하는 나쁜 형식. –