2010-03-20 2 views
62

저는 Android 플랫폼 용 Java에서 대화 형 게임 성능 튜닝을하고 있습니다. 가끔씩 가비지 수집을 위해 그리기 및 상호 작용에 어려움이 있습니다. 일반적으로 1 초의 10 분의 1 미만이지만 때로는 매우 느린 장치의 경우 최대 200ms가 될 수 있습니다.Java 게임에서 가비지 수집 지연을 방지하려면 어떻게해야합니까? (우수 사례)

ddms 프로파일 러 (Android SDK의 일부)를 사용하여 내 메모리 할당이 어디에서 왔는지 파악하고 내 드로잉 및 논리 루프에서이를 제거합니다. 같은 일

최악의 범죄자가 있었다 짧은 루프, 루프가 실행 된 모든 단일 시간이 할당 된 iterator 있었다

for(GameObject gob : interactiveObjects) 
    gob.onDraw(canvas); 

. 내 객체에 배열 ( ArrayList)을 사용하고 있습니다. 내 루프에서 나무 나 해시를 원한다면 자바 콜렉션 프레임 워크를 사용하는 대신 조심하거나 다시 구현해야 할 필요가 있다는 것을 알고있다. 여분의 가비지 콜렉션을 가질 여력이 없기 때문이다. 우선 순위 대기열을 볼 때 그럴 수 있습니다.

또한 Canvas.drawText을 사용하여 점수와 진행 상황을 표시하려는 문제가 있습니다. 이

canvas.drawText("Your score is: " + Score.points, x, y, paint); 

Strings 때문에, char 배열과 StringBuffers 온통 할당됩니다 그것을 작동하도록, 나쁘다. 몇 가지 텍스트 표시 항목이 있고 프레임을 초당 60 회 실행하면 합계가 시작되어 가비지 수집 딸꾹질이 증가합니다. 여기서 가장 좋은 선택은 char[] 배열을 유지하고 int 또는 double을 수동으로 디코드하고 문자열을 시작과 끝으로 연결하는 것입니다. 뭔가 깨끗한 것이 있는지 듣고 싶습니다.

나는이 문제를 다루는 다른 사람들이 있어야한다는 것을 알고 있습니다. 어떻게 처리하고 Java 또는 Android에서 대화식으로 실행하기 위해 발견 한 함정 및 모범 사례가 무엇입니까? 이러한 gc 문제는 나를 수동 메모리 관리를 그리워 할만큼 충분하지만 그리 많지는 않다.

+3

이보기 무효화 및 GC와 로맹 가이의 이야기 마지막 Googe I/O에 할당 뷰어 도구에 대한 섹션을 참조 할 수 있습니다. co.kr/events/io/2009/sessions/TurboChargeUiAndroidFast.html 공식 Android 성능 가이드 라인 : http://developer.android.com/guide/practices/design/performance.html 지금은 향상된 기능을 사용하지 않는 것이 좋습니다. 반복자 반복자 할당 때문에 표준 배열의 for-loop 구문. 문자열을 결합하려면 +로 연결하는 대신 StringBuilder를 사용하십시오 (한 번 할당하고 다시 필요할 때 지워야 함). –

+3

지난 몇 년 동안 Google의 중요한 세션이 Chris Pruett의 게임에 관한 세션이었습니다. 실시간 게임의 성능에 대한 좋은 단서가 많이 있습니다. http://code.google.com/intl/de-DE/events/io/2009/sessions/WritingRealTimeGamesAndroid.html – Moritz

+0

@Moritz의 링크가 업데이트되었습니다. http : // www.google.com/events/io/2010/sessions/writing-real-time-games-android.html – maaartinus

답변

55

나는 ... (다시 게임의 perfs를 죽일해야 한 지점 또는 다른과 에서 GC를 실행하여야한다) GC'ing 오브젝트를 방지하는 가장 좋은 방법은 단순히 자바 모바일 게임에 근무했습니다 우선 게임 루프에서 게임을 만드는 것을 피하십시오.

가이 처리 할 "깨끗한"방법이 없다 내가 먼저 예를 들어 줄거야 ...

일반적으로 당신은, 말하자면, (50,25)에서 화면에 4 공 (70,32가), (16,18), (98,73).

n = 4; 
int[] { 50, 25, 70, 32, 16, 18, 98, 73 } 

당신 "팝업"사라 2 공, 당신 INT는 []이된다 : 음, 여기 (이 예제를 위해 간체) 당신의 추상화입니다

n = 3 
int[] { 50, 25, 98, 73, 16, 18, 98, 73 } 

(알 어떻게 돈 네 번째 공 (98,73)을 "청소"하는 것에 대해서도 신경 쓰지 않고, 우리가 남긴 공의 수를 계속 추적합니다.)

슬프게도 물체를 수동으로 추적합니다. 모바일 장치를 사용하는 현재 잘 수행되고있는 대부분의 Java 게임에서이 작업이 어떻게 수행되는지를 설명합니다.문자열에 대한 지금

, 여기에 내가 할 줄 작업은 다음과 같습니다

게임 초기화시
  • , 당신이 BufferedImage[10]에 저장하는 것이 DrawText에 (...)한 번만 숫자 0 ~ 9를 사용하여 predraw 정렬. 게임 초기화시
  • 가 한 번 을 predraw "당신의 점수는 다음과 같습니다"는 "당신의 점수는 :"경우
  • 정말 다시 그려야 할 때 (예를 들어, 그것은 투명한 때문에), 다음 사전에서 다시 그리기 - 저장된 BufferedImage
  • 루프 "귀하의 점수 : 다음에 숫자를 계산하고 각 숫자를 수동으로 하나씩 (각 시간을 해당 숫자 (0 ~ 9)를 복사하여 BufferedImage[10]을 미리 저장해 두었습니다.

drawtext (...) 글꼴을 다시 사용하고 주 루프 중에 정확히 0 개의 개체를 만들었습니다 (이므로 drawtext (...)에 대한 호출을 피할 수 있습니다.) 그 자체는 일 수 있고,는 아주 잘 생성되고, 잘, 불필요한 허튼 소리이다.

을 "점수를 그리는 제로 객체 생성"이 의 또 다른 "장점은"그것은 정말 조심 캐싱의, 글꼴에 대한주의 이미지 캐싱 및 재사용 정말 "수동 객체 할당/해제"이되지 않는 것입니다.

"깨끗한"것이 아니라 "좋은 습관"이 아니지만 그것이 최고급 모바일 게임 (예 : Uniwar)에서 이루어진 방법입니다.

그리고 빠릅니다. 빠른 속도로. 보다 빠르면 객체 생성과 관련된 내용이 모두입니다.

추 신 : 몇 가지 모바일 게임을 신중하게 살펴보면 실제로 글꼴은 실제로 시스템/자바 글꼴이 아니라 각 게임에 맞게 특별히 제작 된 픽셀 완벽한 글꼴이라는 것을 알 수 있습니다 (여기서는 예제를 제공했습니다. 시스템/자바 글꼴을 캐시하는 방법에 대해 설명하지만 분명히 픽셀 완벽한/비트 맵 글꼴을 캐시/재사용 할 수도 있습니다. 이제 점수를 그리기

final class PrefixedInt implements CharSequence { 

    private final int prefixLen; 
    private final StringBuilder buf; 
    private int value; 

    public PrefixedInt(String prefix) { 
     this.prefixLen = prefix.length(); 
     this.buf = new StringBuilder(prefix); 
    } 

    private boolean hasValue(){ 
     return buf.length() > prefixLen; 
    } 

    public void setValue(int value){ 
     if (hasValue() && this.value == value) 
      return; // no change 
     this.value = value; 
     buf.setLength(prefixLen); 
     buf.append(value); 
    } 


    // TODO: Implement all CharSequence methods (including 
    // toString() for prudence) by delegating to buf 

} 

// Usage: 
private final PrefixedInt scoreText = new PrefixedInt("Your score is: "); 
... 
scoreText.setValue(Score.points); 
canvas.drawText(scoreText, 0, scoreText.length(), x, y, paint); 

:

+3

안드로이드 개발자를위한 행운의 그래픽 코드는 자바의 경량 프레임 워크가 아닌 Skia 네이티브 라이브러리에 있습니다. 결과적으로 메모리와 성능에 대한 걱정없이 화면에 다양한 변형과 ​​버퍼, 심지어는 텍스트까지 처리 할 수 ​​있습니다. 이 문제는 수동 앤티 앨리어싱 또는 수작업 변환 행렬에 픽셀 버퍼를 할당해야하는 경우에 발생합니다. 수동으로 추적 된 객체의 캐시가 있습니다. Strings와 StringBuffers는 똑같은 대우를 필요로한다고 생각합니다. 자세한 내용 주셔서 감사합니다. – Brian

4

당신이 제안되었다으로 텍스트를 미리 렌더링하고 싶지 않은 경우는, drawText는 우리가 우리 자신의 스마트 구현을 할 수 있다는 것을 의미 어떤 CharSequence 받아 (buf 내부 배열이 성장해야 할 수도 있고, drawText까지) 어떤 할당도 발생하지 않습니다.

+1

'buf.append (value)'를 호출하면'String' 객체의 할당이 발생합니다. 'append' 메쏘드는'int' 값을'String.valueOf (int)'에 의해'String'으로 변환합니다. – branoholy

+0

String.format (...)의 성능은 * 하나 * 문자열 만 할당합니까 ?? – peterk

3

GC 일시 중지를 피하는 것이 중요 할 경우 일시 중지가 중요하지 않은 지점에서 의도적으로 GC를 트리거하는 것이 좋습니다. 예를 들어, 쓰레기 집약적 인 "showScores"기능이 게임이 끝날 때 사용되면, 점수 화면을 표시하고 다음 게임을 시작하는 것 사이에 추가로 200ms의 지연이 발생하여 지나치게 산만 해지지 않습니다. System.gc() 점수 화면이 그려지면

그러나이 트릭을 사용하는 경우 GC 일시 중지가 성가신 일이없는 지점에서만주의해야합니다. 그리고 핸드셋의 배터리를 소모 할까 걱정되는 경우에는하지 마십시오.

다중 사용자 또는 비대화 형 응용 프로그램에서 이렇게하지 마십시오. 대부분 이렇게하면 응용 프로그램이 전반적으로 느려질 수 있기 때문입니다.

+0

이것은 GC를 호출 할 때 GC가 실행되는 것을 보증하지 않습니다. 그것은 10ms - 1 분 후 어느 곳에서나 작동 할 수 있습니다 (정확한 측정은 확실하지 않음). 그러나 어느 방법 이건, GC 실행을 호출 할 수도 있고 게임이 시작된 후에 실행이 끝나기 때문에 이것은 실제로 훌륭한 해결책이 아닙니다. –

+10

@ While-E :'System.gc()'가 반환 될 때 콜렉션이 실행되었거나 콜렉션이 실행되지 않았 음을 보증합니다. 내 말은,'System.gc()'는 아무 것도하지 않을 것이라는 보장이 없다는 것입니다. 그렇다면 무엇이든지 함수가 반환하기 전에 완료 될 것입니다. ([docs] (http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#gc())에서 "컨트롤이 메서드 호출에서 반환되면 Java Virtual Machine은 폐기 된 모든 객체에서 공간을 되 찾는 데 최선의 노력을 기울였습니다. ") GC 스레드를 생성하고 즉시 반환하지 않습니다. – cHao

+0

@cHao : 나는 이것을 깨닫지 못했다. 주제에 대해 나에게 계몽 해 주셔서 감사합니다! –

7

나는 최소한 String.format의 쓰레기없는 버전을 만들었습니다. 여기에서 찾으십시오 : http://pastebin.com/s6ZKa3mJ (독일어 의견에 변명하십시오).

이처럼 사용

GFStringBuilder.format("Your score is: % and your name is %").eat(score).eat(name).result 

모든 것은 char[] 배열에 기록됩니다. 모든 쓰레기를 없애기 위해 정수에서 문자열로 수동으로 변환 (숫자로)을 구현해야했습니다. 등 HashMap, ArrayList 모든 자바 데이터 구조가 기본 유형을 처리하기 위해 권투를 사용하기 때문에

그 외에도에서, 나는, SparseArray 가능한을 사용합니다. 상자에 넣을 때마다 int에서 Integer까지이 Integer 개체는 GC로 정리해야합니다. 이것은 2 세 질문은 비록

+1

GitHub에 버전을 올려서 공유하고 싶습니까? (아니면 그냥 자랑하고 있니?) – Brian

+1

[Here it is ...] (http://pastebin.com/s6ZKa3mJ) 현재로서는 정수 및 문자열과 포맷 옵션을 지원하지 않습니다. 지금까지 그들을 필요로했다. 나중에 더 추가 할 것입니다. 제발 독일어로 의견을 변명해라 :) –

15

...

유일한, 그리고 GC 지연이 (시작시 포함) 다소 정적으로 필요한 모든 객체를 할당하여 GC 자체를 피하고 피할 수있는 가장 좋은 방법. 필요한 모든 객체를 미리 만들고 삭제하지 마십시오. 기존 개체를 다시 사용하려면 개체 풀링을 사용하십시오.

코드에 대해 가능한 모든 최적화를 수행 한 후에도 결국 일시 중지 될 수 있습니다. 귀하의 앱 코드 이외의 다른 것들은 여전히 ​​내부적으로 GC 객체를 생성하고 있습니다. 결국 쓰레기가 될 것입니다. 예 : Java 기본 라이브러리. 간단한 List 클래스를 사용하더라도 일 수 있습니다 garbages를 만듭니다. Java API를 호출하면 garbages가 생성 될 수 있습니다. 자바를 사용하는 동안 이러한 할당은 피할 수 없습니다.

또한 Java는 GC를 사용하도록 설계되었으므로 실제로 GC를 피하려고하면 기능 부족으로 문제가 발생합니다. (심지어 List 클래스는 피해야합니다.) 은 GC가이므로 모든 라이브러리 일 수 있습니다. 따라서 은 실질적으로/아니요. 라이브러리가 없습니다. GC 기반 언어에 대한 GC 회피는 일종의 미친 시도입니다.

궁극적으로 유일한 실용적인 방법은 메모리를 직접 제어 할 수있는 낮은 레벨로 이동하는 것입니다. C 계열 언어 (C, C++ 등). NDK로 가십시오.

이제 구글은 많이 일시 정지 줄일 수 증가 (동시?) GC를 출하하고있다. 어쨌든 증분 GC는 시간이 지남에 따라 GC로드를 배포하는 것을 의미하므로 배포가 이상적이지 않은 경우 여전히 일시적인 중단이 표시됩니다. 또한 GC 성능 자체는 일괄 처리 및 분배 작업 오버 헤드의 영향이 적기 때문에 부작용이 줄어 듭니다.

2

반복자 할당에 관해서는 ArrayList의 반복자를 피하는 것이 쉽습니다. http://code.google 대신

for(GameObject gob : interactiveObjects) 
    gob.onDraw(canvas); 

당신은

for (int i = 0; i < interactiveObjects.size(); i++) { 
    interactiveObjects.get(i).onDraw(); 
}