지난 기사에서는 JVM 메모리 모델에 대한 관련 지식을 소개했습니다. 실제로 런타임 시 상수 풀의 동적 삽입, 직접 메모리 등 더 깊이 있게 소개할 수 있는 내용도 있습니다. 이전 블로그를 개선할 시간입니다. 오늘은 JVM의 몇 가지 가비지 수집 전략을 소개하겠습니다.
1. finalize() 메소드
GC 전략을 소개하기 전에 먼저 GC의 finalize 메소드를 소개하겠습니다. 객체에 참조가 없으면 객체는 일반적으로 재활용되지만 객체가 재활용되기 전에 일부 리소스를 닫거나 재활용을 방지하기 위해 객체를 부활시키는 등 일부 작업을 수행하려면 어떻게 해야 할까요? 이는 finalize 메소드가 사용되는 경우입니다. finalize 메소드는 Object 클래스에 정의된 메소드로, 모든 객체에 이 메소드가 있음을 의미합니다. 하지만 이 메서드는 한 번만 호출됩니다. 개체가 부활한 후 다시 죽으면 개체가 두 번째로 재활용될 때 finalize 메서드가 호출되지 않으며 우선 순위가 상대적으로 낮으며 반드시 실행될 것이라는 보장도 없습니다. 실행되므로 finalize 메소드를 사용하지 않는 것이 좋습니다. 정리하자면 3가지 기능이 있습니다. ①, 이전에 GC를 호출한 적이 있습니다. ② 한 번만 호출됩니다. ③.신뢰할 수 없으며 실행을 보장할 수 없습니다. . finalize 사용법은 다음 코드를 참고하세요.
1 public class FinalizeTest { 2 3 private static FinalizeTest test; 4 /** 5 * VM参数:-XX: +PrintGCDetails -Xmx=1M -Xms=1M 6 * 7 * @param args 8 */ 9 public static void main(String[] args) { 10 //先对test对象赋值 11 test = new FinalizeTest(); 12 int _1m = 1024 * 1024; 13 //将test置为null,便于回收 14 test = null; 15 try { 16 System.gc(); 17 //模拟睡眠5s,finalize优先级较低,保证finalize能执行 18 Thread.sleep(5000); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 if (test != null) { 23 System.out.println("first,i am alive"); 24 }else{ 25 System.out.println("first,i am dead"); 26 } 27 //由于test在finalize方法里复活了,再次将test置为null 28 test = null; 29 try { 30 System.gc(); 31 Thread.sleep(5000);//模拟睡眠5s,让GC回收 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 if (test != null) { 36 System.out.println("second,i am alive"); 37 }else{ 38 System.out.println("second,i am dead"); 39 } 40 41 } 42 @Override 43 protected void finalize() throws Throwable { 44 test = this ; 45 System.out.println("finalize excuted"); 46 super.finalize(); //调用父类的finailize方法 47 } 48 }
이 코드를 실행한 결과는 다음과 같습니다.
finalize 메소드가 실행된 후 테스트 객체가 재활성화되는 것을 볼 수 있습니다. , 먼저 i am이 살아 있다고 인쇄됩니다. 그러나 두 번째 GC에서는 finalize 메서드가 실행되지 않아 두 번째로 i am dead가 인쇄되었습니다. 앞에서 언급했듯이 finalize는 우선순위가 낮고 신뢰할 수 없습니다. Thread.sleep(5000)이 없으면 코드와 결과를 살펴보겠습니다.
1 public class FinalizeTest { 2 3 private static FinalizeTest test; 4 /** 5 * VM参数:-XX: +PrintGCDetails -Xmx=1M -Xms=1M 6 * 7 * @param args 8 */ 9 public static void main(String[] args) { 10 //先对test对象赋值 11 test = new FinalizeTest(); 12 int _1m = 1024 * 1024; 13 //将test置为null,便于回收 14 test = null; 15 try { 16 System.gc(); 17 //模拟睡眠5s,finalize优先级较低,保证finalize能执行 18 //不执行睡眠操作,Thread.sleep(5000); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 if (test != null) { 23 System.out.println("first,i am alive"); 24 }else{ 25 System.out.println("first,i am dead"); 26 } 27 //由于test在finalize方法里复活了,再次将test置为null 28 test = null; 29 try { 30 System.gc(); 31 //不执行睡眠操作,Thread.sleep(5000);//模拟睡眠5s,让GC回收 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 if (test != null) { 36 System.out.println("second,i am alive"); 37 }else{ 38 System.out.println("second,i am dead"); 39 } 40 41 } 42 @Override 43 protected void finalize() throws Throwable { 44 test = this ; 45 System.out.println("finalize excuted"); 46 super.finalize(); //调用父类的finailize方法 47 } 48 }
실행 결과는 다음과 같습니다.
여기서는 매우 쉬울 수 있습니다. 마무리 방법의 우선순위가 상대적으로 낮다는 것을 분명히 알 수 있습니다.
이 예제에 대한 고찰: 이 예제의 첫 번째 코드는 "Java Virtual Machine의 심층 이해"의 코드를 참조하여 구현되었지만 항상 두 가지 질문이 있다고 느낍니다. 테스트 개체가 존재하는 이유는 무엇입니까? 정적으로 수정된 멤버 변수로? 정적 수정이라면 메소드 영역이 있는데, 메소드 영역의 GC 효과는 일반적으로 그다지 좋지 않습니다. 다른 하나는 멤버 변수 형태로 존재하기 때문에 finalize를 재활용할 때 현재 객체 자체의 재활용을 반영하지 못하기 때문에 이번 예시는 별로 좋지 않다는 생각이 듭니다.
2. 참조 계산 방법
참조 계산 방법은 현재 일반적으로 사용되지 않는 초기 GC 재활용 알고리즘입니다. 주요 아이디어는 다음과 같습니다. 각 개체는 초기 값이 0인 참조 카운터를 유지합니다. , 객체가 참조되면 객체의 참조 카운터가 1만큼 증가합니다. 참조되지 않으면 객체의 참조 카운터가 1만큼 감소합니다. 객체의 참조 카운터가 0이 되면 해당 객체는 재활용 가능 으로 간주됩니다. 이 방법을 사용하면 장점과 단점이 분명합니다. 구현이 간단하고 효율성이 높다는 점입니다. 단점은 순환 참조가 있어 메모리 오버플로가 발생할 수 있다는 것입니다.
3. 표시 및 청소 방법
표시 및 청소 방법은 이름별로 "표시"와 "지우기"의 두 단계로 구분됩니다. 먼저 모든 생존자를 표시합니다. 표시가 완료되면 표시된 모든 개체가 균일하게 지워집니다. 그렇다면 어떤 물건이 재활용될 수 있는지 여부를 어떻게 판단할 수 있을까요? GC 동안 순회는 일련의 GC 루트 루트 노드에서 시작됩니다. 순회 중에 이동한 경로를 참조 체인이라고 합니다. 객체에 GC 루트와 관련된 참조 체인이 없으면 해당 객체를 사용할 수 없습니다. 재활용이 가능한 것으로 판단되는 이 알고리즘은 루트 검색 알고리즘이라고도 합니다. 그렇다면 어떤 객체가 GC Roots 객체가 될 수 있나요? Java 언어에서 GC Roots로 사용할 수 있는 개체에는 다음 네 가지 유형이 있습니다. GC 루트 사용.
로컬 메소드 스택에서 JNI(네이티브 메소드)가 참조하는 객체
Mark-and-Sweep 메소드의 알고리즘 다이어그램은 다음과 같습니다.
# 🎜🎜##🎜🎜 # 참고: 본 글의 GC 재활용 알고리즘 사진은 네티즌의 글(여기 클릭)에서 옮겨온 것입니다. 네티즌의 사진 내용도 원본과 일치하지만 색상이 다릅니다. 다른.
4. 신세대의 복사 방식메모리를 동일한 크기의 블록 2개로 나누어서만 사용 한 번에 한 블록씩 GC 중에 살아남은 모든 개체를 매번 다른 영역에 복사한 다음 메모리를 정리합니다
. 이들은 모두 메서드 영역과 스택의 참조 개체입니다. 복사 방법의 장점은 구현이 간단하고 재활용이 빠르며 메모리 조각화가 없다는 것입니다. 그러나 한 번에 하나의 블록만 사용되므로 메모리 활용도가 낮습니다. 복제 알고리즘의 개략도는 다음과 같습니다: 관련 권장 사항:
jvm 가비지 수집 알고리즘 # 🎜🎜#Java 가비지 수집 메커니즘 학습 요약 공유
위 내용은 JAVA 가상 머신 연구 노트: JVM 메모리 모델의 가비지 수집 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!