>  기사  >  Java  >  미션 크리티컬 Java 애플리케이션을 위한 가비지 수집 최적화에 대한 자세한 설명(2부)

미션 크리티컬 Java 애플리케이션을 위한 가비지 수집 최적화에 대한 자세한 설명(2부)

黄舟
黄舟원래의
2017-03-23 11:03:441094검색

미션 크리티컬 Java 애플리케이션을 위한 가비지 수집 최적화(1부)

CMS(Parallel Mark Sweep) 수집기

CMS 가비지 수집기는 최초로 널리 사용되는 저지연 수집기입니다. Java 1.4.2에서 사용 가능하지만 처음에는 그다지 안정적이지 않습니다. 이러한 문제는 Java 5까지 해결되지 않았습니다.

CMS 컬렉터 이름에서 알 수 있듯이 병렬 방식을 사용합니다. 대부분의 재활용 작업은 사용자 요청을 처리하는 작업자 스레드와 병렬로 실행되는 GC 스레드에 의해 완료됩니다. 구세대의 오리지널 싱글 stop-the-world 재활용 프로세스는 2개의 짧은 세계 정지 일시 정지와 5개의 병렬 단계로 나뉩니다. 이러한 병렬 단계 동안 원래 작업자 스레드는 일시 중지되지 않고 평소대로 실행됩니다.

다음 매개변수를 사용하여 CMS 재활용기를 활성화합니다.

-XX:+UseConcMarkSweepGC

위 테스트 프로그램을 다시 적용하고 로드를 늘리면 다음과 같은 결과가 나타납니다.

그림 4 힙 크기가 최적화되고 50시간 동안 CMS를 사용하는 JVM의 GC 동작(-Xms1200m -Xmx1200m -XX:NewSize=400m -XX:MaxNewSize=400m -XX:SurvivorRatio=6 -XX:+UseConcMarkSweepGC ) )

보시다시피 Old Generation GC의 8s Pause가 사라졌습니다. 이제 이전 세대 수집 프로세스에는 2번의 일시 중지만 있으며(이전의 경우 50시간 동안 5번의 일시 중지가 발생함) 모든 일시 중지는 1초 이내입니다.

기본적으로 CMS 수집기는 ParNew(GC 알고리즘)를 사용하여 차세대 수집을 처리합니다. ParNew가 CMS와 함께 실행되는 경우 CMS 사이에 추가 조정이 필요하기 때문에 일시 중지 시간이 CMS가 없을 때보다 약간 길어집니다. 지난 테스트 결과와 비교해 본 문제는 신세대 평균 정지시간이 소폭 증가한 점에서 확인할 수 있다. 신세대 Pause Time에서는 Outlier가 자주 나타나는데, 이 문제는 여기서도 발견할 수 있다. 특이치는 약 0.5초에 도달할 수 있습니다. 그러나 이러한 일시 중지는 많은 애플리케이션에서 충분히 짧으므로 CMS/ParNew 조합은 대기 시간이 짧은 좋은 최적화 옵션 역할을 할 수 있습니다.

CMS 컬렉터의 심각한 결함은 Old Generation 공간이 가득 차면 CMS를 시작할 수 없다는 것입니다. 이전 세대가 가득 차면 CMS를 시작하기에는 너무 늦습니다. VM은 일반적인 "세계 중지" 전략을 사용해야 합니다("동시 모드 실패" 기록이 GC 로그에 표시됨). . 저지연 목표를 달성하려면 Old Generation 공간 사용량이 특정 임계값에 도달하면 CMS Collector가 시작되어야 하며 이는 다음 설정을 통해 달성됩니다.

-XX:CMSInitiatingOccupancyFraction=80

이는 Old Generation 공간이 한 번 80%가 사용되면 CMS 수집기가 실행됩니다. 우리 애플리케이션에서는 이 값(기본값)을 사용하면 됩니다. 그러나 임계값을 너무 높게 설정하면 "동시 모드 실패"가 발생하여 장기간 구세대 GC가 일시 중지됩니다. 반면, 너무 낮게 설정하면(활성 공간 크기보다 작게) CMS가 항상 병렬로 실행되어 특정 CPU 코어가 GC에 완전히 사용될 수 있습니다. 대화형 메서드나 타이머를 통해 특수한 작업을 실행하는 등 애플리케이션의 객체 생성 및 힙 사용 동작이 급격하게 변하는 경우 위의 두 가지 문제를 피하면서 적절한 임계값을 설정하기가 어렵습니다.

Shadow of Fragmentation

그러나 CMS의 가장 큰 문제점 중 하나는 구세대 힙 공간을 정리하지 못한다는 점입니다. 이로 인해 힙 조각화가 발생하여 시간이 지남에 따라 심각한 서비스 저하가 발생할 수 있습니다. 이 문제는 두 가지 요인으로 인해 발생할 수 있습니다. 오래된 세대 공간이 부족하고 CMS가 자주 재활용되는 것입니다. 첫 번째 요소는 ParallelGC 수집기에 필요한 공간보다 더 큰 구세대 힙 공간을 늘려서 개선할 수 있습니다(처음 몇 장의 그림에서 볼 수 있듯이 1024M에서 1200M로 늘렸습니다). 두 번째 문제는 앞서 언급한 것처럼 각 세대의 공간을 적절하게 나누어 최적화할 수 있다. 실제로 이것이 구세대 GC의 빈도를 얼마나 줄일 수 있는지 확인할 수 있습니다.

CMS를 사용하기 전에 각 세대의 힙 크기를 합리적으로 조정하는 것이 중요하다는 것을 증명하기 위해 먼저 그림 1을 기반으로 CMS 컬렉터를 직접 사용하는 방법을 살펴보겠습니다(힙 최적화가 거의 없음). 위의 원칙을 따르지 않으면 발생하는 상황:

그림 5 힙 크기가 최적화되지 않은 GC 동작 및 CMS 사용 후 메모리 조각화로 인한 성능 저하(14시간부터 시작) )

이 설정을 사용한 부하 테스트에서 JVM이 거의 14시간 동안 안정적으로 작동할 수 있다는 것은 분명합니다(프로덕션 환경과 더 작은 부하 조건에서는 이러한 양성 불안정 단계가 더 오래 지속될 수 있습니다). 다음으로 갑자기 남은 시간의 거의 절반을 차지하는 긴 GC 일시 중지가 여러 번 발생합니다. 이전 세대의 일시정지 시간이 10초 이상에 달할 뿐만 아니라, 새로운 세대의 일시정지 시간도 몇 초에 달하게 됩니다. 왜냐하면 컬렉터가 신세대에서 구세대로 물건을 옮기기 위해서는 구세대 공간을 탐색하는 데 오랜 시간을 소비해야 하기 때문이다.

CMS低延迟优点的代价就是内存碎片。这个问题可以最小化,但是不会彻底消失。你永远不知道它什么时候会被触发。然而,通过合理的优化与监控可以控制它的风险。

G1(Garbage First)回收器的希望

G1回收器设计的目的就是保证低延迟的同时而没有堆碎片风险。因此,Oracle把它作为CMS的一个长期取代。G1可以避免碎片风险是因为它会整理堆空间。对于GC暂停来说,G1的目标并不是使暂停时间最小化,而是设置一个时间上限,使GC暂停尽量满足这一上限值。

在将G1回收器用于测试程序中并与上述其他经典回收器做对比之前,先总结两点关于G1的重要信息。

  • Oracle在Java 7u4中开始支持G1。为了使用G1你应该将Java 7更新到最新。Oracle的GC团队一直致力于G1的研发,在最新的Java更新中(本文编写时最新版本是7u7到7u9),G1的改进很显著。另一方面,G1无法在任何Java 6版本中使用,而且到目前更优越的Java 7不可能向后移植到Java 6中。

  • 前面关于调节各代空间大小的优化对G1来说已经淘汰了。设置各代空间大小与设置暂停目标时间相冲突会使G1回收器偏离原本的设计目标。使用G1时,可以使用“-Xms”和“-Xmx”设置整体的内存大小,也可以设置GC暂停目标时间(可选),对G1来说不用设置其他选项。与ParallelGC回收器的AdapativeSizingPolicy类似,它自适应地调整各代空间大小来满足暂停目标时间。

遵循这些原则后,G1回收器在默认配置下的结果如下:

图6 最小配置(-Xms1024m -Xmx1024 -XX:+UseG1GC)的JVM在G1下26小时内的GC性能

在这个例子中,我们使用了默认的GC暂停目标时间200ms。从图中可以看到,平均时间与这个目标比较吻合,最长GC暂停时间与使用CMS回收器差不多(图4)。G1明显可以很好地控制GC暂停,与平均时长相比,离群值也相当少。

另一方面,平均GC暂停时间要比CMS回收器长很多(270 vs 100ms),而且更频繁。这意味着GC累积暂停时间(也就是GC本身所占总时间)是使用CMS的4倍以上(6.96% vs 1.66%)。

与CMS一样,G1也分为GC暂停阶段和并行回收阶段(不暂停任务)。同样与CMS类似,当堆占用比达到一定门限后,它才启动并行回收阶段。从图6可以看到,1GB的可用内存到目前为止并没有完全使用。这是因为G1的默认占用比门限值要比CMS低很多。也有人指出,一般来说较小的堆空间就可以满足G1的需求。

垃圾回收器的定量比较

下面的表格总结了Oracle Java 7中4种最重要的垃圾回收器在测试中的关键性能指标。在同样的应用程序上,进行相同的负载测试,但是负载的级别不同(由第2列的垃圾创建速率体现)。

表 几种垃圾回收器的比较

所有的回收器都运行在1GB的堆空间上。传统的回收器(ParallelGC、ParNewGC和CMS)另外使用下面的堆设置:

-XX:NewSize=400m -XX:MaxNewSize=400m -XX:SurvivorRatio=6

而G1回收器没有额外的堆大小设置,并且使用默认的暂停目标时间200ms,也可以显示设置:

-XX:MaxGCPauseMillis=200

从表中可以看到,传统回收器在新生代回收上(第3列)时间差不多。对ParallelGC和ParNewGC来说是差不多的,而CMS实际上也是使用ParNewGC去回收新生代。然而,在新生代GC暂停中,将新生代存活对象移入老年代需要ParNewGC和CMS的协同。这样的协同引入额外的代价,也就导致CMS的新生代GC暂停时间要略长。

第7列是GC暂停所耗费的时间占总时间的百分比,这个值可以很好地反映GC的总时间代价。因为并行GC总时间(最后一列)以及引入的CPU占用代价可以忽略。按前文所述,优化堆大小后老年代GC次数会变得很少,这样第7列的值主要由新生代GC暂停总时间所决定。新生代暂停总时间是新生代暂停(连续)时长(第3列)与暂停次数的乘积。新生代暂停频率与新生代空间大小有关,对传统回收器来说,这个大小是相同的(400MB)。因此,对传统回收器来说,第7列的值或多或少地反映着第3列的值(负载差不多的情况)。

CMS의 장점은 열 6에서 명확하게 볼 수 있습니다. 즉, 더 짧은(10배 더 낮은) 구세대 GC 일시 중지를 위해 약간 더 긴 총 시간 비용을 교환합니다. 많은 실제 응용 프로그램의 경우 이는 좋은 절충안입니다.

그렇다면 G1 컬렉터는 우리 애플리케이션에서 어떤 성능을 발휘할까요? 열 6(및 열 5)에서 볼 수 있듯이 G1 수집기는 이전 세대 GC 일시 중지 시간을 줄이는 데 CMS 수집기보다 더 나은 작업을 수행합니다. 그러나 7열에서도 볼 수 있듯이 매우 높은 비용을 지불합니다. 동일한 로드에서 GC의 총 시간 비용은 7%를 차지하는 반면 CMS는 1.6%만 차지합니다.

다음 글에서는 G1이 더 높은 GC 시간 비용을 발생시키는 조건을 살펴보고, 다른 수집기(특히 CMS 수집기)와 비교하여 G1의 장단점도 분석하겠습니다. 이것은 크고 가치 있는 주제입니다.

요약 및 전망

모든 클래식 Java GC 알고리즘(SerialGC, ParallelGC, ParNewGC 및 CMS)의 경우 각 세대의 힙 공간 크기를 최적화하는 것이 중요합니다. 그러나 많은 실제 애플리케이션에서는 프로그램이 충분히 합리적인 최적화를 수행하지 않았습니다. 그 결과 애플리케이션 성능이 충분히 최적화되지 않고 운영이 저하됩니다(잘 모니터링하지 않으면 성능 손실이 발생하고 심지어 일정 기간 동안 프로그램이 중단될 수도 있음).

각 세대의 힙 공간 크기를 최적화하면 애플리케이션 성능이 크게 향상되고 긴 GC 일시 중지 횟수가 최소화될 수 있습니다. 그런 다음 긴 GC 일시 중지를 없애려면 지연 시간이 짧은 수집기를 사용해야 합니다. CMS는 (지금까지) 선호되고 효율적인 저지연 수집기였습니다. 대부분의 경우 CMS로 충분합니다. 합리적인 최적화를 통해 장기적인 안정성을 보장할 수 있지만 힙 조각화가 발생할 위험이 있습니다.

대안으로 현재 G1 컬렉터(Java 7u9)가 지원되고 사용 가능한 옵션이지만 여전히 개선의 여지가 있습니다. 그 결과는 많은 응용 프로그램에서 허용되지만 CMS 수집기와는 잘 비교되지 않습니다. 장점과 단점에 대한 자세한 내용은 주의 깊게 연구할 가치가 있습니다

위 내용은 미션 크리티컬 Java 애플리케이션을 위한 가비지 수집 최적화에 대한 자세한 설명(2부)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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