source

Java 가비지 컬렉션은 순환 참조와 함께 어떻게 작동합니까?

factcode 2022. 8. 21. 14:07
반응형

Java 가비지 컬렉션은 순환 참조와 함께 어떻게 작동합니까?

제가 알기로는 Java의 가비지 컬렉션은 다른 어떤 오브젝트가 해당 오브젝트를 '지키고' 있지 않은 오브젝트를 정리합니다.

제 질문은, 만약 우리가 이런 것을 가지고 있다면 어떻게 될까요?

class Node {
    public object value;
    public Node next;
    public Node(object o, Node n) { value = 0; next = n;}
}

//...some code
{
    Node a = new Node("a", null), 
         b = new Node("b", a), 
         c = new Node("c", b);
    a.next = c;
} //end of scope
//...other code

a,b , , , , 입니다.c가비지가 수집되어야 하지만 모두 다른 개체에서 참조되고 있습니다.

Java 가비지 컬렉션은 이 문제를 어떻게 해결합니까?(혹은 단순히 메모리 유출입니까?)

Java의 GC는 가비지 컬렉션 루트에서 시작하는 체인을 통해 도달할 수 없는 개체를 "쓰레기"로 간주하므로 이러한 개체가 수집됩니다.비록 물체가 서로 가리키며 순환을 형성할 수 있지만, 그것들이 뿌리에서 잘려나간다면 그것들은 여전히 쓰레기입니다.

"부록 A: Java Platform Performance에서의 가비지 컬렉션에 관한 진실"의 도달 불능 객체에 대한 섹션을 참조하십시오. 살벌한 디테일을 위한 전략과 전술.

예 Java 가비지 컬렉터는 순환 참조를 처리합니다!

How?

가비지 컬렉션 루트(GC 루트)라고 불리는 특별한 오브젝트가 있습니다.이들은 항상 도달할 수 있으며 자체 루트에 있는 개체도 도달할 수 있습니다.

단순한 Java 애플리케이션에는 다음 GC 루트가 있습니다.

  1. 주 방법의 로컬 변수
  2. 메인 스레드
  3. 주 클래스의 정적 변수

여기에 이미지 설명 입력

더 이상 사용되지 않는 개체를 확인하기 위해 JVM은 마크 앤 스위프 알고리즘이라고 불리는 것을 간헐적으로 실행합니다.다음과 같이 동작합니다.

  1. 알고리즘은 GC 루트를 시작으로 모든 오브젝트 참조를 통과하여 검출된 모든 오브젝트를 활성으로 마킹합니다.
  2. 표시된 개체가 사용하지 않는 모든 힙 메모리가 회수됩니다.이것은 단순히 자유로 표시되며 기본적으로 사용되지 않는 물체는 제거됩니다.

따라서 GC 루트에서 도달할 수 없는 오브젝트가 있는 경우(자체 참조 또는 순회 참조가 되어 있는 경우라도), 가비지 수집이 이루어집니다.

물론 프로그래머가 오브젝트를 참조 해제하는 것을 잊으면 메모리 누수가 발생할 수 있습니다.

여기에 이미지 설명 입력

출처 : Java 메모리 관리

정답입니다.설명하는 가비지 컬렉션의 특정 형식을 "참조 카운트"라고 합니다.가장 단순한 경우에서 작동하는 방식(최소한 개념적으로 대부분의 최신 참조 계수 구현은 실제로 상당히 다르게 구현됩니다)은 다음과 같습니다.

  • 객체에 대한 참조가 추가될 때마다(예를 들어 객체가 변수 또는 필드에 할당되거나 메서드에 전달되는 등), 참조 카운트는 1씩 증가합니다.
  • 오브젝트에 대한 참조가 삭제될 때마다(메서드가 반환되거나 변수가 범위를 벗어나거나 필드가 다른 오브젝트에 다시 할당되거나 필드가 포함된 오브젝트가 가비지를 수집됨), 참조 카운트는 1씩 감소합니다.
  • 참조 수가 0에 도달하면 개체에 대한 참조가 더 이상 없으므로 더 이상 사용할 수 없으므로 가비지이며 수집할 수 있습니다.

이 간단한 전략에는 A가 B를 참조하고 B가 A를 참조할 경우 두 참조 카운트가 모두 1보다 작을 수 없으며, 이는 수집되지 않는다는 것을 의미합니다.

이 문제에 대처하려면 다음 4가지 방법이 있습니다.

  1. 무시해요.메모리가 충분하고, 사이클이 작고, 빈도가 낮고, 실행 시간이 짧다면 사이클을 수집하지 않아도 됩니다.셸 스크립트 인터프리터에 대해 생각해 봅시다.셸 스크립트는 보통 몇 초 동안만 실행되며 메모리를 많이 할당하지 않습니다.
  2. 참조 카운팅 가비지 컬렉터를 사이클에 문제가 없는 다른 가비지 컬렉터와 결합합니다.예를 들어 CPython의 메인 가비지 수집기는 참조 카운트 수집기이지만 트레이스 가비지 수집기를 실행하여 사이클을 수집합니다.
  3. 사이클을 검출합니다.안타깝게도 그래프에서 주기를 탐지하는 작업은 다소 비용이 많이 듭니다.특히 트레이스 콜렉터와 거의 같은 오버헤드가 필요하므로 이들 중 하나를 사용하는 것이 좋습니다.
  4. 1970년대 이후 사이클 검출과 참조 카운트를 1개의 조작으로 조합하여 개별적으로 실시하거나 트레이스 콜렉터를 실시하는 것보다 훨씬 저렴한 매우 흥미로운 알고리즘이 다수 개발되어 왔습니다.

덧붙여서, 가비지 콜렉터를 실장하는 다른 주요한 방법은, 트레이스입니다.트레이스 콜렉터는 도달 가능성 개념을 기반으로 합니다.항상 도달할 수 있는 루트 세트(글로벌 상수, 또는Objectclass, current lexical scope, current stack frame)을 사용하여 루트세트에서 도달 가능한 모든 객체를 추적하고 루트세트에서 도달 가능한 객체 등에서 도달 가능한 모든 객체를 추적합니다.이러한 오브젝트는 트랜시티브클로징이 될 때까지 계속됩니다.그 폐쇄에 포함되지 않은 것은 모두 쓰레기입니다.

사이클은 그 자체 내에서만 도달할 수 있으며 루트 세트에서는 도달할 수 없기 때문에 수집됩니다.

가비지 컬렉터는 CPU 레지스터, 스택 및 글로벌 변수와 같이 항상 "도달 가능"으로 간주되는 일부 "루트" 장소 집합에서 시작합니다.이 기능은 해당 영역에서 포인터를 찾고 해당 영역이 가리키는 모든 포인터를 반복적으로 찾아냅니다.그걸 다 발견하면 다른 건 다 쓰레기야

물론 속도를 위해 여러 가지 변형이 있습니다.예를 들어, 대부분의 최신 가비지 컬렉터는 "세대적"으로 객체를 세대로 나눈다는 것을 의미하며, 객체가 나이가 들수록 가비지 컬렉터는 그 객체가 아직 유효한지 여부를 알아내려고 하는 시간이 점점 더 길어집니다.오래됐다면 가능성이 높다고 생각하기 시작할 뿐입니다.더 오래 살 수 있을 것 같아서 다행이에요.

그러나 기본적인 생각은 그대로입니다.이것은, 당연하게 사용되고 있는 몇개의 루트 세트로부터 시작해, 그 외의 사용 가능한 것을 찾기 위해서 모든 포인터를 쫓는 것에 근거하고 있습니다.

흥미로운 것은 차치하고, 사람들은 종종 가비지 콜렉터의 이 부분과 리모트 프로시저 호출과 같은 오브젝트의 마셜링 코드와의 유사성에 놀란다.어떤 경우든 루트 집합에서 시작하여 포인터를 추적하여 참조하는 다른 모든 개체를 찾습니다.

Java GC는 실제로 설명대로 동작하지 않습니다.보다 정확하게 말하면, 이러한 오브젝트는, 「GC 루트」라고 불리는 베이스의 오브젝트로부터 개시해, 루트에서 도달할 수 없는 오브젝트를 수집한다고 하는 것입니다.
GC 루트에는 다음과 같은 것이 포함됩니다.

  • 정적 변수
  • 현재 실행 중인 스레드 스택에 있는 로컬 변수(모든 해당 'this' 참조 포함)

따라서 이 경우 메서드의 마지막에 로컬 변수 a, b 및 c가 범위를 벗어나면 3개의 노드에 대한 참조를 직간접적으로 포함하는 GC 루트는 더 이상 존재하지 않으므로 가비지 수집의 대상이 됩니다.

TouphBeer의 링크에는 더 자세한 정보가 있습니다.

기사(더 이상 제공되지 않음)에서는 가비지 콜렉터에 대해 자세히 설명합니다(개념적으로는...).몇 가지 구현이 있습니다.)투고와 관련된 부분은 "A.3.4 Unreachable"입니다.

A.3.4 도달 불능 객체에 대한 강력한 참조가 더 이상 존재하지 않으면 도달 불능 상태가 됩니다.오브젝트가 도달할 수 없는 경우 수집 대상이 됩니다.문구를 주의해 주세요.물건이 수집 대상이라고 해서 즉시 수집되는 것은 아닙니다.JVM은 오브젝트에 의해 소비되는 메모리가 즉시 필요할 때까지 수집을 지연할 수 있습니다.

가비지 컬렉션은 일반적으로 "다른 항목이 해당 개체를 '지정'하지 않는 경우 해당 개체를 정리"(참조 카운트)하는 것을 의미하지 않습니다.가비지 컬렉션은 대략 프로그램에서 도달할 수 없는 개체를 찾는 것을 의미합니다.

이 예에서는 a, b 및 c가 범위를 벗어나면 이러한 오브젝트에 더 이상 액세스할 수 없기 때문에 GC에서 이러한 오브젝트를 수집할 수 있습니다.

빌은 당신의 질문에 직접 대답했어요.Amnon이 말했듯이, 쓰레기 수집에 대한 당신의 정의는 단지 참고 자료일 뿐입니다.마크, 스위프, 복사 컬렉션과 같은 매우 간단한 알고리즘도 순환 참조를 쉽게 처리할 수 있다는 점을 덧붙이고 싶습니다.그럼 마법은 없는 거네요!

[JVM 메모리 모델 다이어그램]

는 GC에 의한 합니다.is usedstack ★★★★★★★★★★★★★★★★★」permanent기타 모든 참조는 고려되지 않습니다.

iOS용[대략] 유지 사이클(Circular References)은 (개발자로서) 참조 카운트와 할당 해제를 담당하는 상황입니다.

언급URL : https://stackoverflow.com/questions/1910194/how-does-java-garbage-collection-work-with-circular-references

반응형