이 기사에서는 먼저 일반적인 가비지 수집 방법을 간략하게 소개한 다음 G1 수집기의 수집 원리와 다른 가비지 수집기와 비교한 장점을 분석하고 마지막으로 몇 가지 튜닝 방법을 제공합니다.
먼저 G1을 이해하기 전에 가비지 컬렉션이 무엇인지 확실히 알아야 합니다. 간단히 말하면, 가비지 컬렉션은 더 이상 메모리에서 사용되지 않는 객체를 재활용하는 것입니다.
가비지 수집의 기본 단계
재활용에는 2단계가 있습니다:
메모리에서 더 이상 사용되지 않는 개체 찾기
이러한 객체가 차지한 메모리를 해제합니다
1. 메모리에서 더 이상 사용되지 않는 객체를 찾습니다
그럼 문제는 어떻게 결정하느냐입니다. 더 이상 사용되지 않는 객체는 무엇입니까?
참조참조 계산 방법
참조 계산 방법 객체가 어떤 객체에서도 참조되지 않는 경우 이 방법의 단점은 루트 검색 알고리즘입니다.
루트 검색 알고리즘의 기본 아이디어는 일련의 "GC Roots"라는 개체를 시작점으로 사용하고 검색은 이 노드에서 아래쪽으로 시작됩니다. 검색을 참조 체인이라고 합니다. 개체에 GC Roots에 연결된 참조 체인이 없으면 이 개체를 사용할 수 없다는 것이 증명됩니다. 이제 가비지 개체를 찾는 방법을 알았으니 이러한 개체를 정리하는 방법은 무엇입니까? >2. 이러한 개체가 차지하고 있는 메모리를 해제합니다
일반적인 방법으로는 복사 또는 직접 정리가 있지만 직접 정리는 메모리 조각화를 유발하므로 정리 및 압축 방법이 일반적으로 생성됩니다. , 재활용 알고리즘에는 세 가지 유형이 있습니다. 🎜>
1. Mark-Copy사용 가능한 메모리 용량을 두 개의 동일한 크기 블록으로 나누어 한 번에 하나만 사용합니다. 🎜>객체를 다른 블록에 복사한 다음 사용된 메모리 공간을 한 번에 비워야 합니다. 장점은 구현이 간단하고 효율성이 높으며 메모리 조각화가 없다는 것입니다.
2. 마킹-클리닝
마킹 및 클리어 알고리즘은 "마킹"과 "클리어링"의 두 단계로 나누어집니다. 재활용이 필요한 객체를 표시하고, 마킹이 완료된 후 객체를 균일하게 정리합니다. 장점은 효율성이 높지만 단점은 메모리 조각화에 취약하다는 것입니다.
3.
마킹 작업은 "mark-clean" 알고리즘과 일치합니다. 후속 작업은 개체를 직접 정리하는 것이 아니라 쓸모 없는 개체를 정리한 후 살아남은 모든 개체를 한쪽 끝으로 이동하고 포인터를 표시합니다. 객체 참조가 업데이트되므로 "mark-clean"보다 효율성이 떨어지지만 세대 기반 가정
이 발생하지 않습니다. 객체의 생존 시간은 길고 짧습니다. 생존 시간이 긴 객체의 경우 GC 횟수를 줄이면 불필요한 오버헤드를 피할 수 있습니다. 메모리는 새로 생성된 객체와 객체를 구세대로 나누어 저장합니다. 상대적으로 짧은 생존 시간을 가지며, Old Generation은 상대적으로 긴 생존 시간을 가진 객체를 저장합니다. 이런 방식으로 매번 Young 세대만 정리하고, Old 세대는 필요할 때만 정리하므로 GC 효율성을 크게 향상시키고 GC 시간을 절약할 수 있습니다.
Java Garbage Collector의 역사
첫 번째 단계, Serial Collector
jdk1.3.1 이전에는 Java Virtual Machine에서 Serial Collector만 사용할 수 있었습니다. 직렬 수집기는 단일 스레드 수집기이지만 "단일 스레드"라는 의미는 가비지 수집 작업을 완료하기 위해 하나의 CPU 또는 하나의 수집 스레드만 사용한다는 의미일 뿐만 아니라 더 중요한 것은 가비지 수집을 수행할 때 모든 다른 작업자 스레드는 수집이 완료될 때까지 일시 중지되어야 합니다.
PS: 직렬 수집기 켜는 방법
-XX:+UseSerialGC
2단계, 병렬(병렬) 수집기
병렬 수집기 처리량 수집기로서 직렬 수집기에 비해 병렬의 주요 장점은 다중 스레드를 사용하여 가비지 정리 작업을 완료한다는 것입니다. 이는 다중 코어의 특성을 최대한 활용하고 gc 시간을 크게 줄일 수 있습니다.
PS: 병렬 컬렉터를 켜는 방법
-XX:+UseParallelGC -XX:+UseParallelOldGC
세 번째 단계, CMS(동시) 컬렉터
CMS 수집기는 Minor GC 동안 모든 애플리케이션 스레드를 일시 중지하고 다중 스레드 방식으로 가비지 수집을 수행합니다. Full GC 중에는 애플리케이션 스레드가 더 이상 일시 중지되지 않습니다. 대신 여러 백그라운드 스레드를 사용하여 이전 세대 공간을 정기적으로 검색하고 더 이상 사용되지 않는 개체를 적시에 재활용합니다.
PS: CMS 컬렉터를 켜는 방법
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
네 번째 단계, G1(동시) 컬렉터
G1 수집기(또는 가비지 우선 수집기)는 매우 큰 힙(4GB 이상)을 처리할 때 일시 중지를 최소화하도록 설계되었습니다. CMS에 비해 장점은 메모리 조각화 발생률이 크게 감소한다는 것입니다.
PS: G1 수집기 활성화 방법
-XX:+G1GC 사용
G1의 첫 번째 논문(부록 1)은 2004년에 출판되었고 2012년에 jdk1.7u4에서만 볼 수 있었습니다. Oracle은 공식적으로 CMS를 대체하기 위해 G1을 jdk9의 기본 가비지 수집기로 전환할 계획입니다. 오라클이 G1을 강력하게 권장하는 이유는 무엇입니까? G1의 장점은 무엇입니까?
우선 G1의 디자인 원칙은 간단하고 실현 가능한 성능 튜닝
개발자는 다음 매개변수만 선언하면 됩니다.
-XX: + UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200
그 중 -XX:+UseG1GC는 G1 Garbage Collector를 켜는 것이고, -Xmx32g 설계된 힙 메모리의 최대 메모리는 32G, -XX:MaxGCPauseMillis=200이다. 최대 GC를 설정합니다. 일시정지 시간은 200ms입니다. 조정이 필요한 경우 메모리 크기가 확실할 때 최대 일시 중지 시간만 수정하면 됩니다.
둘째, G1은 신세대와 구세대 간의 물리적 공간 분할을 폐지한다.
이렇게 하면 더 이상 각 세대를 별도의 공간에 설정할 필요가 없고, 각 세대에 충분한 메모리가 있는지 걱정할 필요가 없습니다.
대신 G1 알고리즘은 힙을 여전히 세대별 수집기에 속하는 여러 영역(Regions)으로 나눕니다. 그러나 이러한 영역 중 일부에는 새로운 세대의 가비지 수집이 여전히 모든 애플리케이션 스레드를 일시 중지하고 살아남은 개체를 이전 세대 또는 생존자 공간에 복사하는 방법을 사용합니다. Old Generation도 여러 영역으로 나누어져 있는데, G1 컬렉터는 한 영역에서 다른 영역으로 객체를 복사하여 정리 작업을 완료합니다. 이는 정상적인 처리 중에 G1이 힙(적어도 힙의 일부) 압축을 완료하므로 cms 메모리 조각화 문제가 발생하지 않음을 의미합니다.
G1에는 Humongous Area라는 특별한 구역도 있습니다. 개체가 파티션 용량의 50% 이상을 차지하는 경우 G1 수집기는 해당 개체를 거대한 개체로 간주합니다. 이러한 거대 개체는 기본적으로 이전 세대에 직접 할당되지만 수명이 짧은 거대 개체인 경우 가비지 수집기에 부정적인 영향을 미칩니다. 이 문제를 해결하기 위해 G1은 거대한 물체를 보관하는 데 사용되는 거대한 영역을 분할합니다. 거대한 개체가 하나의 H 파티션에 들어갈 수 없는 경우 G1은 이를 저장할 연속적인 H 파티션을 찾습니다. 연속적인 H 영역을 찾기 위해 Full GC를 시작해야 하는 경우가 있습니다.
PS: Java 8에서는 영구 생성도 일반 힙 메모리 공간으로 이동되어 메타 공간으로 변경되었습니다.
객체 할당 전략
대형 객체 할당을 얘기하면 객체 할당 전략을 이야기해야 합니다. 3단계로 나누어집니다:
TLAB(Thread Local Allocation Buffer) 스레드 로컬 할당 버퍼
Eden 영역에서의 할당
거대한 영역 할당
TLAB의 목적은 스레드에 대해 로컬로 버퍼를 할당하는 것입니다. 객체가 공유 공간에 할당된 경우 일부 동기화 메커니즘을 사용하여 이러한 공간의 여유 공간 포인터를 관리해야 합니다. Eden 공간에서는 각 스레드마다 객체 할당을 위한 고정된 파티션, 즉 TLAB가 있습니다. 개체를 할당할 때 스레드 간에 동기화가 필요하지 않습니다.
TLAB 공간에 할당할 수 없는 객체의 경우 JVM은 이를 Eden 공간에 할당하려고 시도합니다. Eden 공간이 객체를 수용할 수 없는 경우 Old Generation에서만 공간을 할당할 수 있습니다.
마지막으로 G1은 Young GC와 Mixed GC 두 가지 GC 모드를 제공하며 둘 다 STW(Stop The World)입니다. 아래에서는 이 두 가지 모드를 각각 소개합니다.
Young GC는 주로 Eden 영역에서 GC를 수행하며, 이는 Eden 공간이 소진되면 트리거됩니다. 이 경우 Eden 공간의 데이터는 Survivor 공간으로 이동되며, Survivor 공간이 부족할 경우 Eden 공간의 데이터 중 일부가 Old Generation 공간으로 직접 승격됩니다. Survivor 영역의 데이터는 새로운 Survivor 영역으로 이동되고, 일부 데이터도 Old Generation 공간으로 승격됩니다. 마지막으로 Eden 공간의 데이터는 비어 있고 GC는 작동을 멈추고 애플리케이션 스레드는 계속 실행됩니다.
이때, 신세대 객체만 GC한다면 어떻게 모든 것을 찾을 수 있는지 생각해 볼 필요가 있습니다. 루트 객체? 모든 객체는 Old Generation에 뿌리를 두고 있나요? 이와 같은 스캔에는 많은 시간이 소요됩니다. 그래서 G1에서는 RSet이라는 개념을 도입했습니다. 전체 이름은 Remembered Set이며 해당 기능은 특정 힙 영역을 가리키는 개체 참조를 추적하는 것입니다.
CMS에도 RSet이라는 개념이 있는데, 구세대에도 신세대에 대한 레퍼런스를 기록하는 영역이 있습니다. 이는 Young GC를 수행할 때 루트를 스캔할 때 이 영역만 스캔하면 되며 전체 Old Generation을 스캔할 필요는 없습니다.
그러나 G1에서는 포인트 아웃을 사용하지 않습니다. 이는 하나의 파티션이 너무 작고 너무 많은 파티션이 있기 때문입니다. 포인트 아웃을 사용하면 일부 파티션이 필요하지 않습니다. GC도 모두 스캔되었습니다. 그래서 G1에서는 이를 해결하기 위해 포인트인(point-in)이 사용됩니다. 포인트인은 현재 파티션의 개체를 참조하는 파티션을 의미합니다. 이렇게 하면 이러한 개체를 루트로만 검색하여 잘못된 검색을 방지할 수 있습니다. 젊은 세대가 여러 명 있는데, 새로운 세대 간의 참고자료를 기록해야 합니까? GC가 발생할 때마다 모든 새로운 세대가 스캔되므로 이는 불필요하므로 이전 세대에서 새 세대까지의 참조만 기록하면 됩니다.
참조된 객체가 많은 경우 할당자는 각 참조를 처리해야 하며 할당자 오버헤드 문제를 해결하기 위해 할당자 오버헤드가 매우 커진다는 점에 유의해야 합니다. G1 컨셉, 카드 테이블. 카드 테이블은 파티션을 고정된 크기의 연속 영역으로 논리적으로 나누며, 각 영역을 카드라고 합니다. 카드는 일반적으로 128~512바이트로 더 작습니다. Card Table은 대개 바이트배열이며, 각 파티션의 공간 주소는 Card의 인덱스(즉, 배열 첨자)로 식별됩니다. 기본적으로 각 카드는 참조되지 않습니다. 주소 공간이 참조되면 이 주소 공간에 해당하는 배열 인덱스의 값이 "0"으로 표시됩니다. 즉, 더티로 표시되고 참조됩니다. 또한 RSet는 배열 첨자를 기록합니다. 일반적인 상황에서 이 RSet은 실제로 해시 테이블이고, 키는 다른 영역의 시작 주소이고, 값은 세트이며, 내부 요소는 카드 테이블의 인덱스입니다.
젊은 GC 단계:
1단계: 루트 검색
정적 및 로컬 개체 검색
2단계 : Update RS
더티 카드 큐 업데이트 RS 처리
3단계: RS 처리
Young Generation에서 Old Generation을 가리키는 객체 감지
4단계: 개체 복사
살아남은 개체를 생존/기존 영역에 복사
5단계: 참조 큐 처리
소프트 참조, 약한 참조 , 가상 참조 처리
Mix GC는 일반적인 차세대 가비지 수집을 수행할 뿐만 아니라 일부 백그라운드 스캔을 재활용합니다. 이전 세대 파티션으로 표시된 스레드.
그 GC 단계는 2단계로 나누어진다:
글로벌 동시 마킹(global Concurrent Marking)
생존 복사 object (evacuation)
Mix GC를 수행하기 전, Global Concurrent Marking을 먼저 수행합니다. 글로벌 동시 마킹의 실행 과정은 어떻게 되나요?
G1 GC에서는 Mixed GC에 대한 마킹 서비스를 주로 제공하며 GC 공정에서 꼭 필요한 부분은 아닙니다. 전역 동시 마킹의 실행 과정은
초기 표시(STW)
이 단계에서 G1 GC가 루트를 표시합니다. 이 단계는 일반(STW) 젊은 세대 가비지 수집과 밀접한 관련이 있습니다.
루트 영역 스캔(Root Region Scan)
G1 GC는 처음에 표시된 생존 영역에서 Old Generation에 대한 참조를 스캔하고 참조된 개체를 표시합니다. 이 단계는 애플리케이션(STW 아님)과 동시에 실행되며, 이 단계가 완료된 후에만 다음 STW 신세대 가비지 수집이 시작될 수 있습니다.
동시 표시
G1 GC는 전체 힙에서 액세스 가능한(라이브) 객체를 검색합니다. 이 단계는 애플리케이션과 동시에 실행되며 STW Young Generation Garbage Collection에 의해 중단될 수 있습니다.
최종 표시(Remark, STW)
이 단계는 표시 주기를 완료하는 데 도움이 되는 STW 수집입니다. . G1 GC는 SATB 버퍼를 지우고, 액세스되지 않은 활성 개체를 추적하며 참조 처리를 수행합니다.
Cleanup, STW)
이 마지막 단계에서 G1 GC는 통계 및 RSet 정제의 STW 작업을 수행합니다. 회계 기간 동안 G1 GC는 완전 무료 영역과 혼합 가비지 수집이 가능한 영역을 식별합니다. 정리 단계는 빈 영역을 재설정하고 사용 가능한 목록으로 반환하므로 부분적으로 동시적입니다.
3색 표시 알고리즘
동시 표시에 관해서는 동시 표시의 3색 표시 알고리즘을 이해해야 합니다. 이는 추적 수집기를 설명하는 유용한 방법이며 수집기의 정확성을 추론하는 데 사용할 수 있습니다. 먼저, 객체를 세 가지 유형으로 나눕니다.
검은색: 루트 개체 또는 개체와 해당 하위 개체가 모두 검사됩니다.
회색: 개체 자체가 검사되지만 개체 자체는 검사됩니다. 아직 개체의 하위 개체를 스캔한 후
흰색: 스캔되지 않은 개체입니다. 모든 개체를 스캔한 후 마지막 흰색 개체는 도달할 수 없는 개체, 즉 가비지 개체입니다.
GC가 개체 스캔을 시작하면 아래 단계에 따라 개체를 스캔합니다.
루트 개체는 검정색으로 설정되고 하위 개체는 회색으로 설정됩니다.
회색에서 계속해서 순회하면서 하위 개체를 스캔한 개체를 검정색으로 설정합니다.
연결 가능한 모든 객체를 순회한 후 도달 가능한 모든 객체가 검은색으로 변합니다. 도달할 수 없는 물체는 흰색이므로 청소가 필요합니다.
멋져 보이지만 마킹 프로세스 중에 애플리케이션이 실행 중인 경우 개체 포인터가 변경될 수 있습니다. 이 경우 문제가 발생합니다: 객체 손실 문제
가비지 수집기가 다음 상황을 스캔할 때 다음 상황을 살펴보겠습니다.
이때 애플리케이션은
A.c=C
B.c=null
이렇게 하면 객체의 상태도는 다음과 같이 된다. 🎜>
이때 가비지 컬렉터가 다시 표시하고 스캔하면 다음과 같습니다. 분명히, 이때 C is White는 쓰레기로 간주되어 청소가 필요하며 이는 분명히 불합리합니다. 그렇다면 애플리케이션이 실행 중일 때 GC로 표시된 객체가 손실되지 않도록 하려면 어떻게 해야 할까요? 2가지 방법이 있습니다:논리 프로세서가 8개보다 많은 경우 n 값을 논리 프로세서 수의 약 5/8로 설정합니다. 이는 n 값이 논리 프로세서 수의 약 5/16일 수 있는 대규모 SPARC 시스템을 제외하고 대부분의 경우에 작동합니다.
-XX:ConcGCThreads=n
병렬로 표시된 스레드 수를 설정합니다. n을 병렬 가비지 수집 스레드 수(ParallelGCThreads)의 약 1/4로 설정합니다.
-XX:InitiatingHeapOccupancyPercent=45
표시 주기를 트리거하는 Java 힙 점유 임계값을 설정합니다. 기본 점유량은 전체 Java 힙의 45%입니다.
다음 매개변수는 사용하지 마세요.
-Xmn 옵션이나 -XX:NewRatio와 같은 기타 관련 옵션을 사용하여 젊은 세대 크기를 명시적으로 설정하지 마세요. 고정된 젊은 세대 크기는 일시 중지 시간 목표를 재정의합니다.
Trigger Full GC
어떤 경우에는 G1이 Full GC를 트리거합니다. 이때 G1은 직렬 수집기를 사용하여 가비지 정리를 완료합니다. .GC가 작동 중이며 GC 일시 중지 시간이 두 번째 수준에 도달합니다. 전체 애플리케이션이 일시 중지된 애니메이션 상태에 있으며 어떤 요청도 처리할 수 없습니다. 물론 우리 프로그램은 이를 보고 싶어하지 않습니다. 그렇다면 Full GC가 발생하는 상황은 무엇일까요?
Concurrent 모드 실패
G1은 마킹 사이클을 시작하지만 Mix GC 이전에 Old Generation이 채워지고 G1은 이에 포기합니다. 시간 마크 주기. 이 경우 힙 크기를 늘리거나 주기를 조정해야 합니다(예: 스레드 수 -XX:ConcGCThreads 증가 등).
Promotion 실패 또는 대피 실패
G1은 GC 수행 시 살아남은 객체나 승격된 객체에 대한 메모리가 부족하여 Full GC가 발생합니다. 로그에서 (to-space 소진) 또는 (to-space 오버플로)를 볼 수 있습니다. 이 문제를 해결하는 방법은 다음과 같습니다.
a, -XX:G1ReservePercent 옵션의 값을 늘려서(그리고 이에 따라 총 힙 크기를 늘려서) "대상 공간"에 대해 예약된 메모리 양을 늘립니다.
b, -XX:InitiatingHeapOccupancyPercent를 줄여 표시 주기를 일찍 시작합니다.
c, -XX:ConcGCThreads 옵션의 값을 늘려 병렬 표시 스레드 수를 늘릴 수도 있습니다.
거대 개체 할당 실패
거대 개체가 할당에 적합한 공간을 찾지 못한 경우 Full GC를 시작하여 해당 공간을 해제합니다. 이 경우 거대 객체를 더 이상 할당하지 않거나, 메모리를 늘리거나 -XX:G1HeapRegionSize를 늘려 거대 객체가 더 이상 거대 객체가 되지 않도록 해야 합니다.
G1의 튜닝 연습은 공간이 제한되어 있으므로 여기에 하나씩 나열하지는 않겠습니다. 일상 연습에서 천천히 살펴보세요. 마지막으로 Java 9의 정식 출시를 기대합니다. 기본적으로 G1을 가비지 컬렉터로 사용하는 Java의 성능이 다시 향상될까요?
위 내용은 Java G1 가비지 수집기 구문 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!