>  기사  >  Java  >  가비지 수집기 GC가 Java 처리량에 미치는 영향 테스트

가비지 수집기 GC가 Java 처리량에 미치는 영향 테스트

高洛峰
高洛峰원래의
2017-01-17 15:48:261658검색

메모리 관리 용어집을 보다가 우연히 중국어로 "Pig in the Python(참고: 탐욕스럽고 부족한 뱀이 코끼리를 삼키는 것과 비슷함)"의 정의를 중국어로 발견해서 이 글을 쓰게 되었습니다. . 표면적으로 이 용어는 한 세대에서 다른 세대로 대형 개체를 지속적으로 승격시키는 GC를 의미합니다. 이렇게 하는 것은 마치 비단뱀이 먹잇감을 통째로 삼켜서 소화하는 동안 움직일 수 없는 것과 같습니다.

그 후 24시간 동안 내 마음은 지울 수 없는 이 숨막히는 비단뱀의 이미지로 가득 차 있었습니다. 정신과 의사들이 말했듯이, 두려움을 해소하는 가장 좋은 방법은 그것을 터놓고 이야기하는 것입니다. 그러므로 이 기사. 하지만 우리가 이야기하고 싶은 다음 이야기는 Python이 아니라 GC 튜닝입니다. 나는 신에게 맹세합니다.

GC 일시 중지로 인해 성능 병목 현상이 쉽게 발생할 수 있다는 것은 누구나 알고 있습니다. 최신 JVM은 출시될 때 고급 가비지 수집기와 함께 제공되지만 내 경험에 따르면 특정 애플리케이션에 대한 최적의 구성을 찾는 것은 극히 어렵습니다. 수동 튜닝에는 아직 희미한 희망이 있을 수 있지만 GC 알고리즘의 정확한 메커니즘을 이해해야 합니다. 이와 관련하여 이 기사는 여러분에게 도움이 될 것입니다. 아래에서는 JVM 구성의 작은 변경이 애플리케이션 처리량에 어떤 영향을 미치는지 설명하기 위해 예제를 사용합니다.

GC가 처리량에 미치는 영향을 보여주기 위해 사용하는 애플리케이션은 단순한 프로그램일 뿐입니다. 여기에는 두 개의 스레드가 포함되어 있습니다.

PigEater – 거대한 비단뱀이 크고 살찐 돼지를 먹는 과정을 모방합니다. 코드는 java.util.List에 32MB 바이트를 추가하고 각 삼킨 후 100ms 동안 대기하여 이를 수행합니다.
PigDigester – 비동기식 소화 과정을 시뮬레이션합니다. 소화를 구현하는 코드는 단순히 돼지 목록을 비우도록 설정합니다. 이는 피곤한 프로세스이므로 이 스레드는 참조를 지운 후 매번 2000ms 동안 휴면 상태가 됩니다.
두 스레드 모두 한동안 루프에서 실행되어 뱀이 가득 찰 때까지 먹고 소화합니다. 이를 위해서는 약 5,000마리의 돼지를 먹어야 합니다.

package eu.plumbr.demo;
public class PigInThePython {
  static volatile List pigs = new ArrayList();
  static volatile int pigsEaten = 0;
  static final int ENOUGH_PIGS = 5000;
  public static void main(String[] args) throws InterruptedException {
    new PigEater().start();
    new PigDigester().start();
  }
  static class PigEater extends Thread {
    @Override
    public void run() {
      while (true) {
        pigs.add(new byte[32 * 1024 * 1024]); //32MB per pig
        if (pigsEaten > ENOUGH_PIGS) return;
        takeANap(100);
      }
    }
  }
  static class PigDigester extends Thread {
    @Override
    public void run() {
      long start = System.currentTimeMillis();
      while (true) {
        takeANap(2000);
        pigsEaten+=pigs.size();
        pigs = new ArrayList();
        if (pigsEaten > ENOUGH_PIGS)  {
          System.out.format("Digested %d pigs in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);
          return;
        }
      }
    }
  }
  static void takeANap(int ms) {
    try {
      Thread.sleep(ms);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

이제 이 시스템의 처리량을 "초당 소화될 수 있는 돼지의 수"로 정의합니다. 100ms마다 돼지 한 마리가 이 파이썬에 채워지는 것을 고려하면 이 시스템의 이론적 최대 처리량은 초당 10마리의 돼지에 도달할 수 있음을 알 수 있습니다.

GC 구성 예시

서로 다른 두 가지 구성 시스템을 사용했을 때의 성능을 살펴보겠습니다. 구성에 관계없이 애플리케이션은 8GB RAM을 갖춘 듀얼 코어 Mac(OS X10.9.3)에서 실행됩니다.

첫 번째 구성:

1.4G 힙(-Xms4g -Xmx4g)
2. CMS를 사용하여 이전 세대(-XX:+UseConcMarkSweepGC)를 정리하고 병렬 수집기를 사용합니다. New Generation(-XX:+UseParNewGC) 정리
3. New Generation에 힙(-Xmn512m)의 12.5%를 할당하고, Eden 영역과 Survivor 영역의 크기를 동일하게 제한합니다.
두 번째 구성은 약간 다릅니다.

1.2G 힙(-Xms2g -Xms2g)
2. 신세대와 구세대 모두 Parellel GC(-XX:+UseParallelGC)를 사용합니다.
3. 힙의 75%를 새로운 세대에 할당합니다(-Xmn 1536m)
4. 이제 어떤 구성이 더 나은 성능을 발휘할지(즉, 초당 몇 마리의 돼지를 먹을 수 있는지, 그리고 기억하다)? 칩을 처음 구성에 넣는 사람들은 실망할 것입니다. 결과는 정반대입니다.

1. 첫 번째 구성(대형 힙, 대형 Old Generation, CMS GC)은 초당 8.2마리의 돼지를 먹을 수 있습니다.
2. 두 번째 구성(소형 힙, 대형) 세대, Parellel GC)는 초당 9.2 마리의 돼지를 먹을 수 있습니다

이제 이 결과를 객관적으로 살펴 보겠습니다. 할당된 리소스는 2배 적지만 처리량은 12% 증가합니다. 이는 상식에 어긋나는 일이기 때문에 무슨 일이 일어나고 있는지 더 자세히 분석할 필요가 있다.

GC 결과 분석

사실 그 이유는 복잡하지 않습니다. 테스트를 실행할 때 GC가 하는 일을 주의 깊게 살펴보기만 하면 답을 찾을 수 있습니다. 여기에서 사용하려는 도구를 선택합니다. jstat의 도움으로 그 뒤에 숨은 비밀을 발견했습니다.

jstat -gc -t -h20 PID 1s

데이터를 분석한 결과 구성 1에서 1129 GC 주기(YGCT_FGCT)가 발생하여 총 63.723초가 소요된 것으로 나타났습니다. :

Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
594.0 174720.0 174720.0 163844.1  0.0   174848.0 131074.1 3670016.0  2621693.5  21248.0 2580.9   1006   63.182  116 0.236   63.419
595.0 174720.0 174720.0 163842.1  0.0   174848.0 65538.0  3670016.0  3047677.9  21248.0 2580.9   1008   63.310  117 0.236   63.546
596.1 174720.0 174720.0 98308.0 163842.1 174848.0 163844.2 3670016.0   491772.9  21248.0 2580.9   1010   63.354  118 0.240   63.595
597.0 174720.0 174720.0  0.0   163840.1 174848.0 131074.1 3670016.0   688380.1  21248.0 2580.9   1011   63.482  118 0.240   63.723

두 번째 구성은 총 168번(YGCT+FGCT) 일시 중지되었으며 11.409초만 소요되었습니다.

Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
539.3 164352.0 164352.0  0.0    0.0   1211904.0 98306.0   524288.0   164352.2  21504.0 2579.2 27    2.969  141 8.441   11.409
540.3 164352.0 164352.0  0.0    0.0   1211904.0 425986.2  524288.0   164352.2  21504.0 2579.2 27    2.969  141 8.441   11.409
541.4 164352.0 164352.0  0.0    0.0   1211904.0 720900.4  524288.0   164352.2  21504.0 2579.2 27    2.969  141 8.441   11.409
542.3 164352.0 164352.0  0.0 0.0   1211904.0 1015812.6  524288.0   164352.2  21504.0 2579.2 27 2.969  141 8.441   11.409

두 경우의 작업량이 동일하다는 점을 고려하면 이 돼지 먹기 실험에서 GC가 수명이 긴 개체를 찾지 못하면 가비지 개체를 더 빨리 정리할 수 있습니다. 첫 번째 구성을 사용하면 GC 작업 빈도는 약 6~7회이고, 총 일시 중지 시간은 5~6회가 됩니다.

이 이야기를 하는 데는 두 가지 목적이 있습니다. 가장 중요한 것은, 나는 이 경련을 일으키는 비단뱀을 내 마음에서 없애고 싶었다는 것입니다. 또 다른 확실한 이점은 GC 튜닝이 매우 숙련된 경험이며 기본 개념에 대한 철저한 이해가 필요하다는 것입니다. 이 문서에 사용된 응용 프로그램은 매우 일반적인 응용 프로그램이지만 선택에 따른 다양한 결과도 처리량 및 용량 계획에 큰 영향을 미칩니다. 실제 적용에서는 차이가 훨씬 더 커집니다. 따라서 이러한 개념을 익히는 것은 귀하에게 달려 있습니다. 아니면 일상 업무에만 집중하고 Plumbr이 귀하의 요구에 가장 적합한 GC 구성을 찾도록 할 수도 있습니다.

Java 처리량에 대한 가비지 컬렉터 GC의 영향 테스트와 관련된 더 많은 기사를 보려면 PHP 중국어 웹사이트에 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.