Programing

Java 게임에서 가비지 수집 지연을 방지하려면 어떻게해야합니까?

crosscheck 2020. 11. 25. 07:40
반응형

Java 게임에서 가비지 수집 지연을 방지하려면 어떻게해야합니까? (모범 사례)


저는 Android 플랫폼 용 Java로 인터랙티브 게임 성능을 조정하고 있습니다. 가끔 가비지 수집을위한 그리기와 상호 작용에 딸꾹질이 있습니다. 일반적으로 1/10 초 미만이지만 때로는 매우 느린 장치에서 200ms까지 클 수 있습니다.

ddms 프로파일 러 (Android SDK의 일부)를 사용하여 메모리 할당의 출처를 검색하고 내부 도면 및 논리 루프에서 제거합니다.

최악의 범죄자는 다음과 같은 짧은 루프였습니다.

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

루프가 실행될 때마다 iterator할당 된 곳이 있습니다. ArrayList지금은 내 개체에 배열 ( )을 사용하고 있습니다. 내부 루프에 트리 나 해시를 원하면 추가 가비지 수집을 감당할 수 없기 때문에 Java Collections 프레임 워크를 사용하는 대신 조심하거나 다시 구현해야한다는 것을 알고 있습니다. 우선 순위 대기열을 볼 때 나타날 수 있습니다.

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

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

Strings, char배열 및 StringBuffers모든 것이 작동하도록 할당 되기 때문 입니다. 텍스트 표시 항목이 몇 개 있고 프레임을 1 초에 60 번 실행하면 합산되기 시작하고 가비지 수집 딸꾹질이 증가합니다. 여기서 최선의 선택은 char[]배열 을 유지 하고 직접 int또는 double수동으로 디코딩 하고 문자열을 시작과 끝에 연결하는 것입니다. 더 깨끗한 것이 있는지 듣고 싶습니다.

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


나는 자바 모바일 게임에 근무했습니다 ... (다시하는 객체를 GC'ing 방지하는 가장 좋은 방법 하여야 한 지점에서 GC를 트리거하거나 다른 및 하여야한다 게임의 perfs을 죽일)는 메인 게임을 만들지 않도록하는 것입니다 처음에 루프.

이를 처리하는 "깨끗한"방법은 없으며 먼저 예를 들어 보겠습니다.

일반적으로 화면에는 (50,25), (70,32), (16,18), (98,73)에 4 개의 공이 있습니다. 여기에 추상화가 있습니다 (이 예제를 위해 단순화 됨).

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

사라진 두 번째 공을 "팝"하면 int []는 다음과 같이됩니다.

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

(우리가 네 번째 공 (98,73)을 "청소"하는 것에 대해 신경 쓰지 않는다는 점에 유의하십시오. 우리는 남은 공의 수를 추적합니다).

슬프게도 물체의 수동 추적. 이것은 모바일 장치에 나와있는 최신 성능이 우수한 Java 게임에서 수행되는 방식입니다.

이제 문자열의 경우 다음 작업을 수행합니다.

  • 게임 초기화시 drawText (...)를 사용하여 배열에 저장 숫자 0 ~ 9를 한 번만 사용하여 미리 그립니다 BufferedImage[10].
  • 게임 초기화시 "당신의 점수 :"
  • 경우 "당신의 점수는" 정말 (예를 들어, 그것은 투명한 때문에), 다음에서 다시 그리기를 다시 그려야 할 때 미리 저장된BufferedImage
  • 루프를 사용하여 점수의 자릿수를 계산하고 "Your score is :" 뒤에 모든 자릿수를 하나씩 수동으로 추가 BufferedImage[10]합니다 (사전 저장 한 위치 에서 해당 자릿수 (0-9)를 매번 복사 하여).

당신은 재사용을 얻을 : 이것은 당신에게 두 세계의 최고 제공 DrawText에 (...) 글꼴을하고 있기 때문에 당신이 (당신의 메인 루프 동안 정확히 제로 객체를 생성 또한 호출 회피 ) DrawText에을 (... 하는 자체 수 있습니다 아주 잘 수 쓸데없는 생성, 음, 쓸데없는 쓰레기).

"제로 객체 생성 드로우 스코어 "의 또 다른 "이점"은 세심한 이미지 캐싱 및 글꼴 재사용이 실제로 "수동 객체 할당 / 할당 해제"가 아니라 실제로 세심한 캐싱이라는 것입니다.

"깨끗한"것도 아니고 "좋은 습관"도 아니지만 이것이 바로 Uniwar와 같은 최고 수준의 모바일 게임에서 수행되는 방식입니다.

그리고 그것은 빠릅니다. 빨리. 객체 생성과 관련된 어떤 것보다 빠릅니다 .

추신 : 실제로 몇 가지 모바일 게임을주의 깊게 살펴보면 글꼴이 실제로 시스템 / 자바 글꼴이 아니라 각 게임을 위해 특별히 만들어진 픽셀 완벽한 글꼴이라는 것을 알 수 있습니다 (여기서는 시스템을 캐시하는 방법에 대한 예제를 제공했습니다) / Java 글꼴이지만 픽셀 완벽한 / 비트 맵 글꼴을 캐시 / 재사용 할 수도 있습니다.)


2 년 된 질문이지만 ...

GC 지연을 피하는 유일한 최선의 방법은 필요한 모든 객체를 다소 정적으로 (시작시 포함) 할당하여 GC 자체를 피하는 것입니다. 필요한 모든 개체를 미리 만들고 삭제하지 마십시오. 개체 풀링을 사용하여 기존 개체를 재사용합니다.

어쨌든 코드에서 가능한 모든 최적화를 수행 한 후에도 결국 일시 중지 될 수 있습니다. 앱 코드 이외의 다른 것은 여전히 ​​내부적으로 GC 객체를 생성 하기 때문에 결국 쓰레기가 될 것입니다. 예를 들어, Java base library . 단순한 List클래스 사용해도 쓰레기 가 발생할 수 있습니다. (따라서 피해야합니다) Java API를 호출하면 가비지가 발생할 수 있습니다. 그리고 이러한 할당은 Java를 사용하는 동안 피할 수 없습니다.

또한 Java는 GC를 활용하도록 설계 되었기 때문에 실제로 GC를 피하려고하면 기능 부족으로 문제가 발생합니다. ( List클래스도 피해야 함) GC를 허용 하기 때문에 모든 라이브러리 GC를 사용할 있으므로 사실상 / 실제적으로 라이브러리가 없습니다 . GC 기반 언어에서 GC를 피하는 것은 일종의 미친 시험이라고 생각합니다.

궁극적으로 유일한 실용적인 방법은 메모리를 완전히 제어 할 수있는 낮은 수준으로 내려가는 것입니다. C 계열 언어 (C, C ++ 등)와 같은. 따라서 NDK로 이동하십시오.

노트

이제 Google은 일시 중지를 많이 줄일 수있는 증분 (동시?) GC를 제공하고 있습니다. 어쨌든 증분 GC는 GC 부하를 시간이 지남에 따라 분산하는 것을 의미하므로 배포가 이상적이지 않은 경우 최종 일시 중지가 계속 표시됩니다. 또한 일괄 처리 및 배포 작업 오버 헤드가 적다는 부작용으로 인해 GC 성능 자체가 저하됩니다.


나는 String.format적어도 종류 의 가비지없는 버전을 만들었습니다 . 여기에서 찾을 수 있습니다 : http://pastebin.com/s6ZKa3mJ (독일어 의견을 용서하십시오).

다음과 같이 사용하십시오.

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

모든 것이 char[]배열에 기록됩니다 . 모든 쓰레기를 제거하기 위해 정수에서 문자열로 수동으로 (숫자 단위) 변환을 구현해야했습니다.

그 외에도 , 등과 SparseArray같은 모든 Java 데이터 구조 는 기본 유형을 처리하기 위해 boxing을 사용해야 하기 때문에 가능한 경우 사용합니다. 당신이 박스마다 로는 ,이 객체는 GC에 의해 청소해야합니다.HashMapArrayListintIntegerInteger


제안 된대로 텍스트를 미리 렌더링하고 싶지 않다면, 우리가 그것을 스마트하게 구현할 수 있다는 것을 의미하는 drawText모든 CharSequence것을 받아들 입니다 :

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);

이제 점수를 그려도 할당이 발생하지 않습니다 (처음에 buf내부 배열을 늘려야 할 때 한두 번 정도를 제외하고 drawText는).


In a situation where it is critical to avoid GC pauses, one trick you could use is to deliberately trigger GC at a point where you know that the pause doesn't matter. For example, if the garbage intensive "showScores" function is used at the end of a game, the user won't be overly distracted by an extra 200ms delay between displaying the score screen and starting the next game ... so you could call System.gc() once the scores screen has been painted.

But if you resort to this trick, you need to be careful to do it only at points where the GC pause will not be annoying. And don't do it if you are worried about draining the handset's battery.

And don't do it in multi-user or non-interactive applications because you'll most likely make the application run slower overall by doing this.


Regarding the iterator allocation, avoiding iterators on ArrayList`s is easy. Instead of

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

you can just do

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

참고URL : https://stackoverflow.com/questions/2484079/how-can-i-avoid-garbage-collection-delays-in-java-games-best-practices

반응형