이 글은 기계 번역의 미러 문서이며, 원본 기사로 바로 이동하려면 여기를 클릭해 주세요.

보기: 12807|회답: 0

[자바SE] Java의 가비지 컬렉션 메커니즘을 깊이 이해하세요

[링크 복사]
게시됨 2017. 12. 4. 오후 8:26:51 | | |
1. 쓰레기 재활용 메커니즘의 중요성  
자바 언어의 주목할 만한 특징 중 하나는 가비지 컬렉션 메커니즘의 도입으로, 이는 C++ 프로그래머들에게 가장 까다로운 메모리 관리 문제를 해결하여 자바 프로그래머가 프로그램을 작성할 때 메모리 관리를 더 이상 고려할 필요가 없게 되었습니다. 가비지 컬렉션 메커니즘 때문에 자바의 객체는 더 이상 '범위' 개념을 가지지 않고, 오직 객체의 참조에만 '범위'가 있습니다.가비지 컬렉션은 메모리 누수를 효과적으로 방지하고 유휴 메모리를 효과적으로 활용할 수 있습니다.

ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为“对象游离”。
2. 가비지 컬렉션 메커니즘에서의 알고리즘  
자바 언어 명세서는 JVM에서 어떤 가비지 컬렉션 알고리즘을 사용할지 명시적으로 지정하지 않지만, 모든 가비지 컬렉션 알고리즘은 일반적으로 두 가지 기본 작업을 수행해야 합니다: (1) 쓸모없는 정보 객체를 찾기; (2) 쓸모없는 객체들이 차지하는 메모리 공간을 되찾아, 프로그램이 다시 사용할 수 있도록 합니다.
1. 참조 카운팅 콜렉터
1.1 알고리즘 분석

참조 카운팅은 가비지 콜렉터에서 초기 전략입니다. 이 접근법에서는 힙 내 각 객체 인스턴스에 대한 참조 개수가 있습니다. 객체가 생성되고 객체 인스턴스가 변수에 할당되면, 변수 수는 1로 설정됩니다. 다른 변수가 이 객체에 대한 참조로 할당되면 카운트가 1을 더합니다(a = b, b가 참조한 객체 인스턴스의 카운터는 +1), 객체 인스턴스에 대한 참조가 수명을 초과하거나 새 값으로 설정되면 객체 인스턴스의 참조 카운터를 1로 빼냅니다. 참조 카운터가 0인 객체의 인스턴스는 쓰레기로 수집될 수 있습니다. 객체 인스턴스가 가비지 컬렉션될 때, 참조하는 객체 인스턴스의 참조 카운터는 -1이 됩니다.
1.2 장점과 단점
장점:

참조 카운트 수집기는 프로그램 실행에 매우 빠르게 통합되어 실행할 수 있습니다. 프로그램이 오랜 시간 중단될 필요가 없는 실시간 환경에 유리합니다.
결점:

순환 참조는 감지할 수 없습니다. *부모 객체가 자식 객체에 대한 참조를 가지면, 자식 객체는 다시 부모 객체를 참조합니다. 이렇게 하면 인용 횟수가 0이 될 수 없습니다.
1.3 참조 카운팅 알고리즘은 예를 들어 순환 참조 문제를 해결할 수 없습니다:
/** * Java学习交流QQ群:589809992 我们一起学Java! */public class Main {    public static void main(String[] args) {        MyObject object1 = new MyObject();        MyObject object2 = new MyObject();        object1.object = object2;        object2.object = object1;        object1 = null;        object2 = null;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  
마지막 두 문장은 object1과 object2를 null로 할당하는데, 이는 object1과 object2가 가리키는 객체들을 더 이상 접근할 수 없지만, 서로 참조하기 때문에 참조 카운터가 0이 아니므로 가비지 콜렉터가 절대 재활용하지 않는다는 의미입니다.
2. 추적 수집기 또는 마크 앤 스윕 알고리즘
2.1 루트 탐색 알고리즘


근 탐색 알고리즘은 이산수학의 그래프 이론에서 도입되었으며, 프로그램은 모든 참조 관계를 그래프로 간주합니다. 노드 GC ROOT에서 시작하여 해당 참조 노드를 찾습니다. 이 노드를 찾은 후 그 노드의 참조 노드를 계속 찾습니다. 모든 참조 노드를 탐색하면 나머지 노드는 참조되지 않은 노드, 즉 쓸모없는 노드로 간주됩니다.
GC 루트로 사용할 수 있는 자바
1. 가상 머신 스택에서 참조되는 객체들(로컬 변수 테이블)
2. 메서드 영역에서 정적 속성이 참조하는 객체
3. 메서드 영역에서 상수가 참조하는 객체
4. 로컬 메서드 스택에서 참조된 객체(네이티브 객체)
2.2 추적 알고리즘의 개략적 다이어그램

2.3 마커 클리어 알고리즘 분석

태그 정리 알고리즘은 루트 컬렉션에서 스캔하여 남은 객체를 표시한 후, 위 그림과 같이 태그되지 않은 객체를 재활용하기 위해 전체 공간을 스캔합니다. 태그 정리 알고리즘은 객체를 이동할 필요가 없고, 생존하지 않는 객체만 처리하므로 많은 생존 객체가 있을 때 매우 효율적이지만, 태그 제거 알고리즘이 생존하지 않은 객체를 직접 재활용하기 때문에 메모리 단편화를 일으킵니다.
3. 컴팩트 알고리즘 또는 라벨 마감 알고리즘


태그-피니시 알고리즘은 태그-클리어 알고리즘과 동일한 방식으로 객체를 표시하지만, 삭제 시 다릅니다. 생존하지 않은 객체가 차지한 공간을 되찾은 후, 모든 생존 객체를 왼쪽 끝의 빈 공간으로 이동시키고 해당 포인터를 업데이트합니다. 태그-종료 알고리즘은 태그-퍼지 알고리즘을 기반으로 객체를 이동시키기 때문에 비용이 더 들지만 메모리 단편화 문제를 해결합니다. 컴팩트 알고리즘을 기반으로 한 수집기 구현에서는 일반적으로 핸들과 핸들 테이블이 추가됩니다.
4. 복사 알고리즘 (컴팩트 컬렉터)


이 알고리즘은 핸들의 오버헤드를 극복하고 힙 잔해의 가비지 컬렉션을 해결하기 위해 제안되었습니다. 객체가 가득 차면, 복사 알고리즘에 기반한 가비지 컬렉션이 루트 집합에서 활성 객체를 스캔하여 각 활성 객체를 빈 면으로 복사합니다(활성 객체가 차지하는 메모리 사이에 빈 구멍이 없도록 합니다). 이렇게 하면 빈 표면이 객체 면이 되고, 원래 객체 면이 빈 면이 되며, 프로그램은 새 객체 면에 메모리를 할당합니다. 대응 알고리즘을 기반으로 한 전형적인 가비지 컬렉션은 정지 및 복사 알고리즘으로, 힙을 객체 면과 자유 영역 면으로 나누고, 객체 면과 자유 영역 면을 전환할 때 실행을 일시정지합니다.
5. 세대 수집가


세대별 쓰레기 재활용 전략은 다음과 같은 사실에 기반합니다다양한 물건의 생애 주기는 다릅니다。 따라서 서로 다른 수명 주기를 가진 객체들은 재활용 효율성을 높이기 위해 서로 다른 재활용 알고리즘을 채택할 수 있습니다.
영 제너레이션
1. 새로 생성된 모든 물건은 먼저 젊은 세대에 배치된다. 젊은 세대의 목표는 짧은 생애 주기를 가진 물건들을 가능한 한 빨리 수집하는 것입니다.

2. 신세대 메모리는 8:1:1 비율에 따라 에덴 지역과 생존자 존 2개(생존자0, 생존자 1)로 나뉩니다. 에덴 존 하나, 생존자 존 두 개(일반적으로). 대부분의 오브젝트는 에덴 지역에 생성됩니다. 생존자0 영역도 가득 차면, 에덴 영역과 생존자 0 영역이 다른 생존자 영역으로 복사되고, 그 다음 에덴과 생존자 영역이 비워지고, 생존자0 영역이 비어 있는 후 생존자0 영역과 생존자 1 영역이 교환됩니다. 즉, 생존자 1 구역을 비워두는 식으로 계속 진행하세요.

3. 생존자 1 구역이 에덴과 생존자0의 생존 물건을 보관하기에 충분하지 않을 경우, 남은 물건들은 바로 구 시대로 저장됩니다. 만약 노년도 가득 차면 완전한 GC가 발동되어 신세대와 구세대가 재활용됩니다

4. 신세대의 GC는 마이너 GC라고도 불리며, 마이너GC의 빈도는 비교적 높습니다(에덴 지역이 가득 찼을 때 반드시 발동되는 것은 아닙니다)
구세대

1. 젊은 세대에서 N개의 쓰레기 재활용을 경험한 후에도 살아있는 물건은 이전 세대에 배치됩니다. 따라서 구세대는 수명 주기가 긴 일부 객체에 저장된다고 볼 수 있습니다.

2. 기억 또한 신세대보다 훨씬 크며(대략적인 비율은 1:2), 노년 기억이 가득 차면 주요 GC가 발동됩니다. 즉, 완전한 GC가 활성화되고, 완전한 GC의 빈도는 상대적으로 낮으며, 노년 대상의 생존 기간은 비교적 길고, 생존율 표시가 높습니다.
영구 세대
Java 클래스, 메서드 등과 같은 정적 파일을 저장하는 데 사용됩니다. 영구 생성은 가비지 컬렉션에 큰 영향을 미치지 않지만, 일부 애플리케이션은 Hibernate와 같이 일부 클래스를 동적으로 생성하거나 호출할 수 있으며, 이 경우 런타임 동안 이 새로운 클래스들을 저장하기 위해 비교적 큰 영구 생성 공간을 설정해야 합니다.
3. GC (쓰레기 수거기)신세대 수집가들이 사용하는 수집기: 시리얼, 프라뉴, 패러럴 스캐빈지
올드 에이지 콜렉터가 사용하는 수집기: 시리얼 올드, 패러럴 올드, CMS

직렬 수집기(복제 알고리즘)
신세대 단일 스레드 수집기, 마킹 및 청소는 단일 스레드로 이루어져 단순하고 효율적이라는 장점이 있습니다.
직렬 올드 콜렉터 (라벨-피니시 알고리즘)
올드 에이지 싱글 스레드 콜렉터, 시리얼 콜렉터의 올드 에이지 버전.
ParNew 수집기 (정지 복사 알고리즘)
신세대 컬렉터는 멀티 스레드 버전인 시리얼 콜렉터로 간주할 수 있으며, 멀티코어 CPU 환경에서 시리얼보다 성능이 우수합니다.
병렬 스캐빈지 콜렉터 (스톱-복사 알고리즘)
고처리량과 효율적인 CPU 활용을 위한 병렬 수집기. 처리량은 일반적으로 99%이며, 처리량은 = 사용자 스레드 시간 / (사용자 스레드 시간 + GC 스레드 시간)입니다. 이는 이에 대응하는 높은 상호작용이 필요하지 않은 백그라운드 애플리케이션 같은 시나리오에 적합합니다.
병렬 올드 콜렉터 (스톱-복사 알고리즘)
처리량 우선성을 가진 병렬 수집기의 구형 버전, 병렬 수집기입니다
CMS(동시 마크 스윕) 수집기(마크 클린 알고리즘)
높은 동시성, 낮은 일시정지, 가장 짧은 GC 복구 일시정지 추구, 높은 CPU 사용률, 빠른 응답 시간, 짧은 일시정지 시간, 그리고 높은 응답 시간을 추구하는 멀티코어 CPU
4. GC의 구현 메커니즘물건이 여러 세대에 걸쳐 처리되었기 때문에 쓰레기 수거 구역과 시기도 다릅니다. GC에는 두 가지 유형이 있습니다: 스캐빈지 GC와 Full GC입니다.
스캐빈지 GC
보통 새로운 오브젝트가 생성되었고 에덴 내 공간 신청에 실패하면 스캐빈지 GC가 발동되어 에덴 지역을 GC하고 생존하지 않은 오브젝트를 제거하며 생존자 오브젝트를 서바이버 지역으로 이동시킵니다. 그다음 서바이버의 두 구역을 정리하세요. 이러한 GC는 젊은 세대의 에덴 영역에 대해 수행되며, 기성 세대에는 영향을 미치지 않습니다. 대부분의 객체가 에덴 영역에서 시작되고, 에덴 영역은 많이 할당되지 않기 때문에 에덴 영역의 GC가 자주 수행됩니다. 따라서 에덴을 가능한 한 빨리 자유롭게 만들기 위해서는 빠르고 효율적인 알고리즘을 사용하는 것이 일반적으로 필요합니다.
정식 GC
영, 종신 재직, 정직 등 전체 자료를 정리하세요. Full GC는 전체 힙을 재활용해야 하므로 Scavenge GC보다 느리므로 Full GC의 수를 최대한 줄여야 합니다. JVM 튜닝 과정의 큰 부분은 FullGC의 튜닝입니다. 전면 GC는 다음과 같은 이유로 발생할 수 있습니다:
1. 정식 종신 재직권
2. 퍼름은 가득 쓰여 있습니다  
3. System.gc()는 호출로 표시됩니다  
4. 힙의 도메인 할당 전략은 마지막 GC 이후 동적으로 변화합니다.
5. 자바와 GC는 메모리 누수 문제도 발생할 수 있습니다1. HashMap, Vector 등과 같은 정적 컬렉션 클래스의 사용은 메모리 누수가 가장 취약하며, 이 정적 변수들의 수명 주기는 애플리케이션과 동일하며, 모든 객체를 해제할 수 없습니다. 왜냐하면 항상 Vector 등에 의해 적용되기 때문입니다.
정적 벡터 v = 새로운 벡터(); (int i = 1; i<100; i++) { Object o = new Object();     v.add(o);     o = null; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  
이 예시에서는 코드 스택 내에서 벡터 객체에 대한 참조 v와 객체 객체에 대한 참조 o가 있습니다. For 루프에서는 새로운 객체를 계속 생성하고, 벡터 객체에 추가한 뒤 o 참조를 무효화합니다. 문제는, o 참조가 무효화될 때 GC가 발생하면 우리가 만든 Object 객체를 GC에 의해 재활용할 수 있느냐는 것입니다. 대답은 안 된다야. 왜냐하면 GC가 코드 스택에서 참조를 추적할 때 v의 참조를 찾고, 계속 추적하면 v 참조가 가리키는 메모리 공간 내 객체 객체에 대한 참조가 있다는 것을 알게 되기 때문입니다. 즉, o 참조가 비워졌더라도 여전히 접근할 수 있는 다른 참조가 존재하므로 GC는 이를 해제할 수 없습니다. 이 루프 이후에 Object 객체가 프로그램에 영향을 미치지 않는다면, Java 프로그램에 메모리 누수가 있다고 가정합니다.
2. 다양한 연결, 데이터베이스 연결, 네트워크 연결, IO 연결 등이 호출을 종료 근처로 표시하지 않으며, GC가 재활용하지 않아 메모리 누수가 발생합니다.
3. 리스너 사용은 객체를 해제할 때 리스너가 삭제되지 않을 때 메모리 누수를 유발할 수 있습니다.




이전의:초보자들은 CSS에서 약간의 블랙 테크놀로지
다음:Wanke Cloud 스냅 소프트웨어 바이러스 트로이 목마에 조심하세요!
면책 조항:
Code Farmer Network에서 발행하는 모든 소프트웨어, 프로그래밍 자료 또는 기사는 학습 및 연구 목적으로만 사용됩니다; 위 내용은 상업적 또는 불법적인 목적으로 사용되지 않으며, 그렇지 않으면 모든 책임이 사용자에게 부담됩니다. 이 사이트의 정보는 인터넷에서 가져온 것이며, 저작권 분쟁은 이 사이트와는 관련이 없습니다. 위 내용은 다운로드 후 24시간 이내에 컴퓨터에서 완전히 삭제해야 합니다. 프로그램이 마음에 드신다면, 진짜 소프트웨어를 지원하고, 등록을 구매하며, 더 나은 진짜 서비스를 받아주세요. 침해가 있을 경우 이메일로 연락해 주시기 바랍니다.

Mail To:help@itsvse.com