1. Java 메모리 모델
프로그램을 실행할 때 Java Virtual Machine은 여러 데이터 영역으로 관리하는 메모리를 나눕니다. 이러한 데이터 영역의 분포는 다음과 같습니다.
프로그램 카운터 : 현재 실행 된 바이트 코드를 가리키는 작은 메모리 영역. 스레드가 Java 메소드를 실행하는 경우이 카운터는 가상 머신 바이트 코드 명령어의 주소를 실행 중입니다. 기본 메소드가 실행되면 계산기 값이 비어 있습니다.
Java Virtual Machine 스택 : 스레드는 비공개이며 수명주기는 스레드와 일치합니다. 각 방법이 실행되면 로컬 변수 테이블, 오페라 스택, 동적 링크, 메소드 종료 등과 같은 정보를 저장하기 위해 스택 프레임이 작성됩니다.
로컬 메소드 스택 : 기능은 가상 머신 스택이 가상 머신에 대한 Java 메소드 서비스를 수행하고 로컬 메소드 스택은 사용 된 기본 메소드를 제공한다는 점을 제외하고 가상 머신 스택과 유사합니다.
Java Heap : 모든 스레드에서 공유하는 가장 큰 가상 머신 관리 메모리이며,이 영역은 객체 인스턴스를 저장하는 데 사용 되며이 영역에 거의 모든 객체가 할당됩니다. Java 힙은 메모리 재활용의 주요 영역입니다. 메모리 재활용의 관점에서 볼 때, 대부분의 현재 수집가는 세대 수집 알고리즘을 사용하기 때문에 Java 힙은 또한 새로운 세대와 구식으로 세분 될 수 있습니다. 조금 세분화되면, 생존자 공간에서 생존자 공간 등에 이르기까지 Eden 공간으로 나눌 수 있습니다. Java Virtual Machine Specification에 따르면 Java 힙은 논리적으로 연속적 인 한 물리적으로 불연속적인 공간에있을 수 있습니다.
방법 영역 : Java와 마찬가지로 다양한 스레드에서 공유되며 가상 머신에서로드 된 클래스 정보와 같은 데이터를 저장하는 데 사용됩니다.
런타임 상수 풀, 런타임 상수 풀은 방법 영역의 일부입니다. 클래스 버전, 필드, 메소드, 인터페이스 및 기타 설명 정보 외에도 클래스 파일에는 일정한 풀이 있으며, 이는 컴파일 기간 동안 생성 된 다양한 문자적이고 상징적 참조를 저장하는 데 사용됩니다. 런타임 동안 새로운 상수를 상수 풀에 배치 할 수 있습니다. 가장 일반적으로 사용되는 것은 문자열 클래스의 인턴 () 메소드입니다. 문자열 인스턴스가 인턴을 호출하면 Java는 상수 풀에 동일한 유니 코드 문자열 상수가 있는지 여부를 찾습니다. 있다면 참조를 반환합니다. 그렇지 않은 경우 인스턴스 문자열과 동일한 유니 코드를 추가하고 참조를 반환합니다.
2. 쓰레기 물체를 결정하는 방법
자바 힙에 저장된 몇 가지 객체 인스턴스가 있습니다. 쓰레기 수집기가 힙을 재활용하기 전에 먼저 어떤 객체가 여전히 "살아있는"어떤 객체가 "살아있는"지, 즉 "죽은", 즉 어떤 식 으로든 사용하지 않는 개체를 결정해야합니다.
견적 계산
인용 계수 방법은 구현하기가 간단하고 효율적이며 대부분의 경우 좋은 알고리즘입니다. 원리는 다음과 같습니다. 객체에 참조 카운터를 추가하십시오. 객체를 참조 할 장소가있을 때마다 카운터가 1만큼 증가합니다. 참조가 실패하면 카운터가 1만큼 줄어 듭니다. 카운터 값이 0 인 경우 객체가 더 이상 사용되지 않음을 의미합니다. 참조 계산 방법은 객체 간의 상호 참조 문제를 해결하기가 어렵고 주류 Java 가상 머신은 참조 계산 방법을 사용하여 메모리를 관리하지 않습니다.
접근성 분석 알고리즘
이 알고리즘의 기본 아이디어는 이러한 노드에서 시작하여 "GC Roots"라는 일련의 객체를 시작점으로 검색하는 것입니다. 검색 된 경로를 참조 체인이라고합니다. 객체가 참조 체인없이 GC 루트에 연결되지 않은 경우 (그래프 이론의 단어로, GC 루트에서이 객체에 이르기까지)이 객체를 사용할 수 없음을 증명합니다. 그림에서 볼 수 있듯이, 객체 5, 객체 6 및 객체 7은 서로 관련이 있지만 GC 루트에는 도달 할 수 없으므로 재활용 가능한 물체로 판단됩니다.
Java 언어로 GC Roots로 사용할 수있는 다음 객체는 다음과 같습니다.
가상 머신 스택 (스택 프레임의 로컬 변수 테이블)에서 참조 된 객체.
메소드 영역에서 클래스의 정적 속성에 의해 참조 된 객체.
방법 영역에서 상수에 의해 참조 된 객체.
로컬 메소드 스택에서 JNI (즉, 일반적인 기본 방법)에 의해 참조 된 객체.
이제 문제는 접근성 분석 알고리즘에 객체 사이에 순환 참조 문제가 있습니까? 대답은 그렇습니다. 즉, 객체 사이의 원형 참조에는 문제가 없습니다. GC 루트는 객체 그래프 외부에 특별히 정의 된 "시작점"이며 객체 그래프의 객체에서는 참조 할 수 없습니다.
죽거나 죽지 않는 것
접근성 분석 알고리즘에서 도달 할 수없는 객체조차도 "MIST DIE"가 아닙니다. 현재로서는 "보호 관찰"단계에 있습니다. 객체를 진정으로 선언하려면 적어도 두 가지 마킹 프로세스를 거쳐야합니다. 객체가 접근성 분석을 수행 한 후 GC 루트에 연결된 참조 체인이 없다는 것을 알게되면 처음으로 표시되고 필터링됩니다. 필터링 조건은이 객체가 Finapze () 메소드를 실행 해야하는지 여부입니다. 객체가 Finapze () 메소드를 덮어 쓰지 않거나 Finapze () 메소드가 가상 시스템에서 호출되면 가상 머신은 두 사례를 "실행할 필요 없음"으로 간주합니다. 이 프로그램에서는 Finapze ()를 덮어 쓸 수 있도록 "스릴 넘치는"자가 축합 프로세스를 만들 수 있지만 이것은 단 하나의 기회 일뿐입니다.
/** *이 코드는 두 가지 점을 보여줍니다. * 1. 객체는 GC 일 때 스스로를 저장할 수 있습니다. * 2. 객체의 finapze () 메소드는 시스템에 의해 단 한 번만 자동으로 호출되기 때문에 자기 구속의 기회는 단 하나뿐입니다. pubpc void isapve () {system.out.println ( "예, 아직도 apve :)); } @override protected void finapze () 던지기 {super.finapze (); System.out.println ( "Finapze mehtod executed!"); Finapzeescapegc.save_hook = this; } pubpc static void main (String [] args) 던지기 가능 {save_hook = new FinapzeescapeGc (); // 객체가 처음으로 성공적으로 저장됩니다. save_hook = null; System.gc (); // Finapze 메소드는 우선 순위가 낮기 때문에 0.5 초 동안 일시 중지하여 thread.sleep (500)를 기다립니다. if (save_hook! = null) {save_hook.isapve (); } else {system.out.println ( "아니요, 나는 죽었습니다 :(");} // 다음 코드는 위와 정확히 동일하지만 이번에는 자체 구속이 실패했습니다. save_hook = null; System.gc (); finapze 방법은 우선 순위가 낮기 때문에 (500), Save! if (500); save_hook.isapve (); else {system.out.println (아니요, 죽었습니다 :( ")}실행 결과는 다음과 같습니다.
Finapze Mehtod가 실행되었습니다! 예, 나는 여전히 apve입니다 :) 아니, 나는 죽었다 :(
인용에 대해 이야기합시다
참조 계산 알고리즘을 통해 객체의 참조 수를 판단하든, 객체의 생존이 "참조"와 관련이 있는지 여부를 결정하는 접근성 분석 알고리즘을 통해 객체의 참조 체인이 도달 할 수 있는지 여부를 결정하는지 여부. JDK 1.2 이전에 Java의 참조 정의는 매우 전통적이었습니다. 참조 유형 데이터에 저장된 값이 다른 메모리의 시작 주소를 나타내는 경우이 메모리 조각은 참조를 나타냅니다. JDK 1.2 이후 Java는 참조 개념을 확장하고 참조를 강력한 참조, 소프트 참조, 약한 참조 및 팬텀 참조의 네 가지 유형으로 나누었습니다. 이 네 가지 유형의 기준의 강도는 차례로 점차 약화되었습니다.
• 강력한 인용은 "Object obj = new Object ()"와 같은 프로그램 코드에서 공통적 인 참조를 나타냅니다. 강력한 인용이 여전히 존재하는 한, 쓰레기 수집기는 참조 된 물체를 재활용하지 않습니다.
• 소프트 참조는 유용하지만 필요한 객체를 설명하는 데 사용됩니다. 소프트 참조 관련 객체의 경우 시스템이 메모리 오버 플로우 예외를 갖기 전에 두 번째 재활용을위한 재활용 범위에 나열됩니다. 이 재활용에 대한 메모리가 충분하지 않으면 메모리 오버플로 예외가 발생합니다. JDK 1.2 이후, 소프트 참조를 구현하기 위해 Softreference 클래스가 제공됩니다.
• 약한 참조는 필수 비 필수 물체를 설명하는 데 사용되지만 강도는 소프트 참조보다 약합니다. 약한 참조와 관련된 물체는 다음 쓰레기 수집이 발생할 때까지만 살아남을 수 있습니다. 쓰레기 수집기가 작동하면 현재 메모리가 충분한 지 여부에 관계없이 약한 참조와 관련된 물체가 수집됩니다. JDK 1.2 이후 약한 참조를 구현하기 위해 약한 회의 클래스가 제공됩니다.
• 무효 인용문은 고스트 따옴표 또는 팬텀 따옴표라고도하며 가장 약한 인용 관계입니다. 객체에 가상 참조가 있는지 여부는 생존 시간에 전혀 영향을 미치지 않으며 가상 참조를 통해 객체 인스턴스를 얻을 수 없습니다. 객체에 대한 가상 참조 연관을 설정하는 유일한 목적은 수집가가 객체를 재활용 할 때 시스템 알림을받는 것입니다. JDK 1.2 이후, Phantomreference 클래스는 가상 참조를 구현하기 위해 제공됩니다.
소프트 참조 사용의 예 :
패키지 jvm; import java.lang.ref.softreference; 클래스 노드 {pubpc string msg = "";} pubpc class hello {pubpc static void main (String [] args) {node node1 = new node (); // strong 참조 node1.msg = "node1"; softreference <node> node2 = new softreference <Node> (node1); // 소프트 참조 node2.get (). msg = "node2"; System.out.println (node1.msg); System.out.println (node2.get (). msg);}}출력 결과는 다음과 같습니다.
node2node2
3. 전형적인 쓰레기 수집 알고리즘
1. 마크-스위프 (마크 클리어) 알고리즘
이것은 가장 기본적인 쓰레기 수집 알고리즘입니다. 그것이 가장 기본적인 것으로 알려진 이유는 구현하기가 가장 쉽고 가장 간단한 아이디어이기 때문입니다. 마크 클리어링 알고리즘은 마킹 단계와 청소 단계의 두 단계로 나뉩니다. 마킹 단계의 작업은 재활용 해야하는 모든 물체를 표시하는 것이며, 청소 단계는 표시된 물체가 차지하는 공간을 재활용하는 것입니다. 특정 프로세스는 아래 그림에 나와 있습니다.
그림에서 마크 클리어링 알고리즘을 구현하기가 더 쉽다는 것을 쉽게 볼 수 있지만 메모리 조각을 쉽게 생성하는 것이 심각한 문제가 있습니다. 너무 많은 조각이 후속 프로세스에서 큰 물체에 대한 공간을 할당 할 때 충분한 공간을 찾을 수없고 새로운 쓰레기 수집 조치를 미리 트리거 할 수 없습니다.
2. 알고리즘 복사
Mark-Sweep 알고리즘의 단점을 해결하기 위해 복사 알고리즘이 제안되었습니다. 사용 가능한 메모리를 한 번에 하나의 조각 만 사용하여 용량별로 동일한 크기의 두 조각으로 나눕니다. 이 메모리 조각을 사용하면 여전히 살아있는 객체를 다른 조각으로 복사 한 다음 사용한 메모리 공간을 한 번에 정리하여 메모리 조각화 문제가 발생하지 않도록하십시오. 특정 프로세스는 아래 그림에 나와 있습니다.
이 알고리즘은 구현하기 쉽고 실행하기에 효율적이며 메모리 조각화를 쉽게 생성하지 않지만 사용될 수있는 메모리가 원래의 메모리의 절반으로 축소되기 때문에 메모리 공간을 사용하는 데 비용이 많이 듭니다.
분명히, 복사 알고리즘의 효율성은 생존 객체의 수와 관련이 있습니다. 살아남은 물체가 많이있는 경우 복사 알고리즘의 효율성이 크게 줄어 듭니다.
3. Mark-Compact (Mark-Collation) 알고리즘
복사 알고리즘의 단점을 해결하고 메모리 공간을 최대한 활용하기 위해 마크 컴팩트 알고리즘이 제안됩니다. 알고리즘은 Mark-Sweep과 동일하지만 마크를 완료 한 후에는 재활용 가능한 물체를 직접 정리하지는 않지만 모든 살아있는 물체를 한쪽 끝으로 이동 한 다음 끝 경계 외부의 메모리를 정리합니다. 특정 프로세스는 아래 그림에 나와 있습니다.
4. 세대 수집 알고리즘
Generation Collection 알고리즘은 현재 대부분의 JVM 쓰레기 수집기가 사용합니다. 그것의 핵심 아이디어는 물체 생존의 수명주기에 따라 메모리를 여러 다른 영역으로 나누는 것입니다. 일반적으로 힙 영역은 구세대와 젊은 세대로 나뉩니다. 구식의 특징은 쓰레기를 수집 할 때마다 적은 수의 물체 만 재활용해야한다는 것입니다. 새로운 세대의 특징은 쓰레기를 수집 할 때마다 많은 수의 물체를 재활용해야한다는 것입니다. 그런 다음 가장 적합한 수집 알고리즘을 다른 세대의 특성에 따라 채택 할 수 있습니다.
현재 대부분의 쓰레기 수집가는 새로운 세대의 복사 알고리즘을 채택합니다. 신규 세대에서는 대부분의 물체가 매번 재활용되어야하기 때문에, 즉 복사 해야하는 작업의 수는 작지만 실제로는 새로운 세대의 공간이 1 : 1의 비율에 따라 나뉘 지 않습니다. 일반적으로, 새로운 세대는 더 큰 에덴 공간과 2 개의 작은 생존자 공간 (보통 8 : 1 : 1)으로 나뉩니다. 에덴 공간과 생존자 공간 중 하나가 사용될 때마다 재활용 할 때 여전히 에덴과 생존자에서 살아남는 물체는 다른 생존자 공간에 복사되며 에덴과 방금 사용 된 생존자 공간이 청소됩니다.
노년은 적은 수의 객체 만 매번 재활용되기 때문에 Mark-Compact 알고리즘이 일반적으로 사용됩니다.
Java 메모리 모델 및 쓰레기 수집에 대한 위의 간단한 분석은 내가 공유하는 모든 콘텐츠입니다. 나는 당신이 당신에게 참조를 줄 수 있기를 바랍니다. 그리고 당신이 wulin.com을 더 지원할 수 있기를 바랍니다.