[java] 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, bc쓰레기 수집해야하지만, 그들은 다른 모든 객체에 의해 참조되고있다.

Java 가비지 콜렉션은이를 어떻게 처리합니까? (또는 단순히 메모리 소모입니까?)



답변

Java의 GC는 가비지 콜렉션 루트에서 시작하는 체인을 통해 도달 할 수없는 경우 “가비지”로 간주되므로 이러한 오브젝트가 수집됩니다. 주기를 형성하기 위해 객체가 서로를 가리킬 수도 있지만 루트에서 잘라 내면 여전히 쓰레기입니다.

부록 A : Java 플랫폼 성능의 가비지 콜렉션에 대한 진실 : 전략 및 전술 에서 도달 할 수없는 오브젝트에 대한 섹션을 참조 하십시오.


답변

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

How?

가비지 수집 루트 (GC 루트)라는 특수 객체가 있습니다. 이것들은 항상 접근 할 수 있으며 자체 루트에있는 모든 객체입니다.

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

  1. 주요 방법의 지역 변수
  2. 주요 실
  3. 메인 클래스의 정적 변수

여기에 이미지 설명을 입력하십시오

더 이상 사용하지 않는 오브젝트를 판별하기 위해 JVM은 mark-and-sweep 알고리즘 이라고하는 것을 간헐적으로 실행 합니다 . 다음과 같이 작동합니다

  1. 이 알고리즘은 GC 루트부터 시작하여 모든 객체 참조를 통과하고 발견 된 모든 객체를 살아있는 것으로 표시합니다.
  2. 표시된 오브젝트가 차지하지 않는 모든 힙 메모리가 재 확보됩니다. 단순히 사용되지 않는 것으로 표시되어 있으며 기본적으로 사용되지 않는 객체가 없습니다.

따라서 GC 루트에서 객체에 도달 할 수없는 경우 (자체 참조 또는 순환 참조 인 경우에도) 가비지 수집 대상이됩니다.

물론 프로그래머가 객체의 역 참조를 잊어 버린 경우 메모리 누수가 발생할 수 있습니다.

여기에 이미지 설명을 입력하십시오

출처 : 자바 메모리 관리


답변

가비지 수집기는 CPU 레지스터, 스택 및 전역 변수와 같이 항상 “연결 가능한”것으로 간주되는 일부 “루트”위치에서 시작합니다. 해당 영역에서 포인터를 찾고 그들이 가리키는 모든 것을 재귀 적으로 찾아서 작동합니다. 모든 것이 발견되면 다른 모든 것은 쓰레기입니다.

물론 속도를 위해 대부분 몇 가지 변형이 있습니다. 예를 들어, 대부분의 최신 가비지 수집기는 “세대 적”이므로 개체를 세대로 나눕니다. 개체가 오래되면 가비지 수집기는 해당 개체가 여전히 유효한지 여부를 파악하려고 시도하는 시간이 점점 길어집니다. -오래 살았다면 더 오래 살 가능성이 매우 높다고 가정하기 시작합니다.

그럼에도 불구하고, 기본 아이디어는 동일하게 유지됩니다. 그것은 당연한 것으로 생각되는 일부 루트 세트에서 시작하여 여전히 사용할 수있는 모든 포인터를 쫓는 것을 기반으로합니다.

흥미로운 점 : 사람들은 종종 가비지 수집기의이 부분과 원격 프로 시저 호출과 같은 객체의 마샬링 코드 간의 유사성에 놀랄 수 있습니다. 각각의 경우, 루트 객체 세트에서 시작하여 포인터를 쫓아 참조하는 다른 모든 객체를 찾습니다.


답변

당신이 올바른지. 설명하는 특정 형태의 가비지 콜렉션을 ” 참조 횟수 “라고합니다. 가장 간단한 경우에 개념적으로 작동하는 방법 (적어도, 대부분의 최신 참조 횟수 구현은 실제로는 다르게 다르게 구현 됨)은 다음과 같습니다.

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

그리고이 간단한 전략은 당신이 결정하는 문제를 정확히 가지고 있습니다 : 만약 A가 B를 참조하고 B가 B를 참조한다면, 그들의 참조 카운트는 모두 1보다 작을 수 없으며 , 이는 결코 수집되지 않을 것입니다.

이 문제를 처리하는 방법에는 네 가지가 있습니다.

  1. 무시해. 메모리가 충분하면주기가 작고 드물고 런타임이 짧을 경우주기를 수집하지 않고 벗어날 수 있습니다. 쉘 스크립트 인터프리터를 생각해보십시오. 쉘 스크립트는 일반적으로 몇 초 동안 만 실행되며 많은 메모리를 할당하지 않습니다.
  2. 사이클에 문제가없는 다른 가비지 수집기와 참조 횟수 가비지 수집기를 결합하십시오 . CPython은이를 수행합니다. 예를 들면 다음과 같습니다. CPython의 기본 가비지 수집기는 참조 횟수 수집기이지만 때때로주기를 수집하기 위해 추적 가비지 수집기가 실행됩니다.
  3. 주기를 감지하십시오. 불행하게도, 그래프에서 사이클을 감지하는 것은 다소 비용이 많이 드는 작업입니다. 특히, 추적 콜렉터와 거의 동일한 오버 헤드가 필요하므로 그 중 하나를 사용할 수도 있습니다.
  4. 순진한 방식으로 알고리즘을 구현하지 마십시오. 1970 년대 이래로 사이클 감지와 레퍼런스 카운팅을 단일 작업에서 영리한 방식으로 결합하여 수행하는 것보다 훨씬 저렴한 여러 흥미로운 알고리즘이 개발되었습니다. 별도로 또는 추적 수집기를 수행합니다.

그건 그렇고, 가비지 수집기를 구현하는 다른 주요 방법 (그리고 이미 위에서 몇 번 암시 했음)은 추적 입니다. 추적 수집기는 도달 가능성 개념을 기반으로합니다 . 당신은 몇 가지로 시작 루트 세트 당신이 알고 항상 도달 (전역 상수, 예를 들어, 또는 Object당신이 거기에서 클래스, 현재 어휘 범위, 현재 스택 프레임) 및 추적 루트 세트에서 연결할 수있는 모든 개체를 한 후, 전이 폐쇄가있을 때까지 루트 세트에서 도달 할 수있는 오브젝트 등에서 도달 할 수있는 모든 오브젝트. 폐쇄 되지 않은 모든 것은 쓰레기입니다.

주기는 자체 내에서만 도달 할 수 있지만 루트 세트에서는 도달 할 수 없으므로 수집됩니다.


답변

Java GC는 실제로 설명대로 작동하지 않습니다. “GC 루트”라고하는 기본 개체 집합에서 시작하여 루트에서 도달 할 수없는 개체를 수집한다고 말하는 것이 더 정확합니다.
GC 루트는 다음과 같은 것들을 포함합니다 :

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

따라서 귀하의 경우, 지역 변수 a, b 및 c가 분석법 끝에서 범위를 벗어나면 3 개의 노드 중 하나에 대한 참조를 직접 또는 간접적으로 포함하는 더 이상 GC 루트가 없습니다. 가비지 수집 대상이됩니다.

TofuBeer의 링크는 원하는 경우 더 자세합니다.


답변

이 기사 (더 이상 사용할 수 없음)는 가비지 수집기에 대해 깊이있게 설명합니다 (개념적으로는 몇 가지 구현이 있습니다). 게시물의 관련 부분은 “A.3.4 도달 할 수 없음”입니다.

A.3.4 도달 할 수없는 개체 참조가 더 이상 존재하지 않으면 개체는 도달 할 수없는 상태가됩니다. 개체에 도달 할 수 없으면 수집 대상입니다. 문구에 유의하십시오 : 개체가 수집 대상이라고해서 개체가 즉시 수집되는 것은 아닙니다. JVM은 오브젝트가 소비하는 메모리가 즉시 필요할 때까지 콜렉션을 지연시킬 수 있습니다.


답변

가비지 콜렉션은 일반적으로 “다른 오브젝트가 해당 오브젝트를 가리키고 있지 않다면 일부 오브젝트를 정리합니다”를 의미하지 않습니다 (즉, 참조 횟수). 가비지 콜렉션은 대략 프로그램에서 도달 할 수없는 객체를 찾는 것을 의미합니다.

따라서 귀하의 예에서 a, b 및 c가 범위를 벗어난 후에는 더 이상 이러한 객체에 액세스 할 수 없으므로 GC에서 수집 할 수 있습니다.