1. 가비지 컬렉션 알고리즘의 핵심 아이디어
자바 언어는 사용 중인 객체를 추적하고 더 이상 사용(참조)되지 않는 객체를 찾아 재활용하는 가비지 컬렉션 메커니즘을 확립했습니다. 이 메커니즘은 동적 메모리 할당에서 발생할 수 있는 두 가지 위험, 즉 과도한 메모리 가비지로 인한 메모리 고갈과 부적절한 메모리 해제로 인한 불법 메모리 참조를 효과적으로 방지할 수 있습니다.
가비지 컬렉션 알고리즘의 핵심 아이디어는 가상 머신이 사용할 수 있는 메모리 공간, 즉 힙 공간에서 객체를 식별하는 것입니다. 반대로, 객체가 더 이상 참조되지 않으면 살아있는 객체라고 합니다. 참조는 가비지 객체이며, 참조가 차지하는 공간은 재할당을 위해 재활용될 수 있습니다. 가비지 수집 알고리즘의 선택과 가비지 수집 시스템 매개변수의 합리적인 조정은 시스템 성능에 직접적인 영향을 미치므로 개발자는 더 깊은 이해가 필요합니다.
2. Main GC(Garbage Collector) 실행 조건
JVM은 Secondary GC를 매우 자주 수행하지만 이 GC는 시간이 거의 걸리지 않으므로 시스템에 거의 영향을 미치지 않습니다. 더욱 주목해야 할 점은 메인 GC의 발동 조건이 시스템에 큰 영향을 미치기 때문이다. 일반적으로 메인 GC를 트리거하는 조건은 두 가지입니다.
(1) 애플리케이션이 유휴 상태일 때, 즉 실행 중인 애플리케이션 스레드가 없을 때 GC가 호출됩니다. 우선 순위가 가장 낮은 스레드에서 GC가 수행되므로 다음 조건을 제외하고는 애플리케이션이 바쁜 상태에서는 GC 스레드가 호출되지 않습니다.
(2) Java 힙 메모리가 부족할 경우 GC가 호출됩니다. 애플리케이션 스레드가 실행 중이고 실행 프로세스 중에 새 객체를 생성할 때 이때 메모리 공간이 부족하면 JVM은 강제로 GC 스레드를 호출하여 새 할당을 위해 메모리를 회수합니다. 한 번의 GC 후에도 메모리 할당 요구 사항을 충족할 수 없는 경우 JVM은 추가 시도를 위해 두 번 더 GC를 수행합니다. 요구 사항을 여전히 충족할 수 없는 경우 JVM은 "메모리 부족" 오류를 보고하고 Java 애플리케이션이 중지됩니다. .
메인 GC 수행 여부는 시스템 환경에 따라 JVM에 의해 결정되고, 시스템 환경은 끊임없이 변화하기 때문에 메인 GC의 동작이 불확실하고, 불가피하게 언제 진행될 것인지 예측이 불가능하다. 발생하지만 확실한 것은 장기간 실행되는 애플리케이션의 경우 메인 GC가 반복적으로 수행된다는 것입니다.
3. GC 오버헤드를 줄이기 위한 조치
위의 GC 메커니즘에 따르면 프로그램의 동작은 시스템 환경의 변화에 직접적인 영향을 미쳐 GC 발생에 영향을 미치게 됩니다. GC의 특성에 맞게 디자인하고 코딩하지 않으면 메모리 유지 등의 부정적인 영향이 연쇄적으로 발생하게 됩니다. 이러한 영향을 피하기 위해서는 쓰레기를 줄이고 GC 과정에서 오버헤드를 최대한 줄이는 것이 기본 원칙이다. 구체적인 조치에는 다음과 같은 측면이 포함됩니다.
(1) System.gc()를 명시적으로 호출하지 마십시오
이 함수는 JVM이 메인 GC를 수행하도록 권장합니다. 이는 단지 제안일 뿐 보장은 아닙니다. 많은 경우에 메인 GC를 트리거하여 메인 GC의 빈도를 높입니다. 즉, 간헐적인 일시 중지 횟수가 늘어납니다. 여기서 특별히 주의해야 할 점은 코드에 표시된 System.gc() 호출이 반드시 GC를 수행할 수 있는 것은 아닐 수도 있다는 점입니다. 이는 finalize() 메서드, 즉 적극적으로 System.gc()를 호출하여 확인할 수 있습니다. 매번 반드시 그런 것은 아닙니다. finalize() 메서드가 매번 호출됩니다. finalize() 메서드의 특징은 객체가 재활용되기 전에 finalize() 메서드가 먼저 호출된다는 것입니다.
(2) 임시 객체의 사용을 최소화하세요
임시 객체는 함수 호출에서 빠져나오면 쓰레기가 됩니다. 임시 변수를 적게 사용하는 것은 쓰레기 생성을 줄이는 것과 같으므로 위에서 언급한 두 번째 문제가 길어집니다. .트리거 조건이 발생하는 시간은 메인 GC의 가능성을 감소시킵니다.
(3) 개체를 사용하지 않을 때는 명시적으로 Null로 설정하는 것이 가장 좋습니다
일반적으로 Null 개체는 가비지로 처리되므로 사용하지 않는 개체를 명시적으로 Null로 설정하는 것이 좋습니다. Null. GC 수집기가 가비지를 결정하여 GC의 효율성을 향상시킵니다.
(4) 문자열을 축적하려면 String 대신 StringBuffer를 사용해 보십시오(자세한 내용은 JAVA의 String 및 StringBuffer에 대한 다른 블로그 기사 참조)
String은 고정 길이 문자열 객체이므로 String 객체를 축적하는 경우 , String 개체에서 확장하는 대신 Str5=Str1+Str2+Str3+Str4와 같은 새 String 개체가 다시 생성됩니다. "+" "New" 때문에 이 문을 실행하는 동안 여러 개의 가비지 개체가 생성됩니다. 문자열 개체는 작업 중에 생성되어야 하지만 이러한 전환 개체는 시스템에 실질적인 의미가 없으며 더 많은 쓰레기만 추가할 뿐입니다. 이러한 상황을 방지하려면 대신 StringBuffer를 사용하여 문자열을 축적할 수 있습니다. StringBuffer는 가변 길이이므로 원래 기반으로 확장되고 중간 개체를 생성하지 않습니다.
(5) Integer 및 Long 객체 대신 Int 및 Long과 같은 기본 유형을 사용할 수 있습니다
기본 유형 변수는 해당 객체보다 훨씬 적은 메모리 리소스를 차지합니다. 기본 변수를 사용합니다. 언제 Integer를 사용해야 합니까?
(6) 정적 개체 변수는 가능한 한 적게 사용합니다.
정적 변수는 전역 변수이며 GC에서 재활용되지 않습니다. 항상 메모리를 차지합니다.
(7) 객체 생성 또는 삭제 시간을 분산시키세요
단시간 내에 많은 수의 새로운 객체, 특히 대형 객체 생성에 집중하면 갑자기 대규모 객체가 필요하게 됩니다. 이러한 상황에 직면하면 JVM 전용 Main GC를 수행하여 메모리를 회수하거나 메모리 조각을 통합하여 메인 GC의 빈도를 높일 수 있습니다. 중앙 집중식 객체 삭제에도 동일한 원칙이 적용됩니다. 이로 인해 갑자기 많은 수의 가비지 개체가 나타나게 되고 여유 공간이 필연적으로 줄어들게 되어 다음에 새 개체가 생성될 때 기본 GC를 강제로 수행할 확률이 크게 높아집니다.
4. 가비지 컬렉션 알고리즘
(1) 참조 카운팅 컬렉터
참조 카운팅은 가비지 컬렉션의 초기 전략입니다. 이 접근 방식에서는 힙의 모든 개체에 참조 횟수가 있습니다. 객체가 생성되고 객체에 대한 참조가 변수에 할당되면 객체의 참조 카운트는 1로 설정됩니다. 예를 들어, 새 객체 A a=new A()를 생성한 다음 a가 다른 변수 b에 할당됩니다. 즉, b=a이면 객체 a의 참조 횟수는 +1입니다. 다른 변수에 이 개체에 대한 참조가 할당되면 개수가 1씩 증가합니다. 객체의 참조가 수명을 초과하거나 새 값으로 설정되면 객체의 참조 카운트는 1씩 감소합니다. 예를 들어 b=c인 경우 a의 참조 카운트는 -1입니다. 참조 횟수가 0인 모든 객체는 가비지 수집될 수 있습니다. 객체가 가비지 수집되면 해당 객체가 참조하는 객체의 수가 1씩 감소합니다. 이 접근 방식에서는 한 개체의 가비지 수집으로 인해 다른 개체에 대한 후속 가비지 수집 작업이 발생할 수 있습니다. 예를 들어 A a=new A();b=a; b가 가비지 수집되면 a의 참조 횟수가 0이 되어 a도 가비지 수집됩니다.
방법의 장점: 참조 카운팅 수집기는 빠르게 실행될 수 있으며 프로그램 실행과 얽혀 있습니다. 이 미리 알림은 프로그램을 오랫동안 중단할 수 없는 실시간 환경에서 유용합니다.
방법의 단점: 참조 카운팅은 순환을 감지할 수 없습니다(즉, 둘 이상의 객체가 서로를 참조함). 루프의 예로는 상위 개체가 하위 개체에 대한 참조를 갖고 하위 개체가 차례로 상위 개체를 참조하는 것입니다. 이런 방식으로 실행 프로그램의 루트 개체가 더 이상 접근할 수 없는 경우에도 개체 사용자의 개수가 0이 되는 것은 불가능합니다. 또 다른 단점은 참조 횟수가 증가하거나 감소할 때마다 추가 오버헤드가 발생한다는 것입니다.
(2) 추적 수집기
가비지 감지는 일반적으로 루트 개체 컬렉션을 구축하고 이러한 루트 개체에서 시작하여 연결 가능성을 확인하여 구현됩니다. 실행 프로그램에서 액세스할 수 있는 루트 개체와 개체 사이에 참조 경로가 있으면 개체에 도달할 수 있습니다. 루트 개체는 항상 프로그램에 액세스할 수 있습니다. 이러한 루트 개체부터 시작하여 접촉할 수 있는 모든 개체는 "활성" 개체로 간주됩니다. 만질 수 없는 객체는 더 이상 프로그램의 향후 실행에 영향을 주지 않기 때문에 쓰레기로 간주됩니다.
추적 수집기는 루트 노드부터 개체 참조 그래프를 추적합니다. 추적 중에 발견된 개체는 특정 방식으로 표시됩니다. 일반적으로 객체 자체에 마커를 설정하거나 별도의 비트맵을 사용하여 마커를 설정합니다. 추적이 종료되면 표시되지 않은 개체에는 접근할 수 없으며 수집할 수 있습니다.
기본 추적 알고리즘을 "마크 앤 클리어"라고 합니다. 이름은 정크폰의 두 단계를 나타냅니다. 표시 단계 동안 가비지 수집기는 참조 트리를 탐색하고 발견된 각 개체를 표시합니다. 정리 단계에서는 표시되지 않은 개체가 해제되고 개체를 해제한 후 얻은 메모리가 실행 프로그램에 반환됩니다. JVM(Java Virtual Machine)에서 정리 단계에는 객체의 마무리가 포함되어야 합니다.
자바 가비지 컬렉션에 대한 더 자세하고 자세한 설명과 관련 글은 PHP 중국어 홈페이지를 주목해주세요!