2010-04-12 6 views
20

나는 종종 ("비교적 새로운 객체") 태양 JVM 수명이 짧은 객체에짧은 수명의 개체와 가비지 수집의 차이점을 만드는 이유는 무엇입니까?

  • 왜이다 쓰레기 수명이 긴 개체를보다 더 효율적으로 수집 ("상대적으로 오래된 객체")이 될 수 읽었습니다 그래서?
  • Sun JVM에만 해당합니까? 아니면 일반적인 가비지 수집 원칙의 결과입니까?
+2

내게는 많은 질문에 답하는 것 같다. 긴 풀에 대한 참조를 복사하는 것이 아니라면 에덴 가비지 수집이 생존자 가비지 수집보다 빠릅니다. 그것은 시간이 걸립니다. –

답변

21

대부분의 Java 응용 프로그램은 Java 객체를 만든 다음이를 빨리 삭제합니다. 당신은 한 번 방법을 빠져 나오면 모든 객체가 죽는다. 대부분의 앱은 이러한 방식으로 작동하며 대부분의 사람들은 이러한 방식으로 앱을 코딩하는 경향이 있습니다. 자바 힙은 대체로 영구, 오래된 (오래 살았던) 세대, 젊은 (짧은 살았던) 세대의 3 부분으로 나뉘어져 있습니다. 젊은 세대는 S1, S2, eden으로 더 나뉩니다. 이것들은 단지 힙이다.

대부분의 개체는 젊은 세대에서 만들어집니다. 여기에있는 아이디어는 사물의 사망률이 높기 때문에 빨리 생성하여 사용하고 버리는 것입니다. 속도는 본질적입니다. 개체를 만들면 보조 GC가 발생할 때까지 젊은 세대가 채워집니다. 보조 GC에서는 살아있는 모든 객체가 eden에서 복사되고 S2에서 S1로 복사됩니다. 그런 다음 '포인터'가 에덴과 S2 위에 놓입니다.

모든 복사본이 개체의 나이를 지정합니다. 기본적으로 객체가 32 개의 사본 즉, 32 minor GC이면 GC는 훨씬 오래 걸릴 것으로 예측합니다. 그래서 그것이하는 일은 그것을 구세대로 옮겨서 재임하는 것입니다. 올드 젠은 단지 하나의 큰 공간 일뿐입니다. 오래된 세대가 가득 차면 전체 GC 또는 주요 GC가 오래된 세대에서 발생합니다. 복사 할 다른 공간이 없기 때문에 GC는 압축해야합니다. 이것은 사소한 GC보다 훨씬 느리므로 그 일을 더 자주하지 않아야합니다. 당신이 오래 사는 개체를 많이 가지고 있음을 알고 있다면 당신은 조정

java -XX:MaxTenuringThreshold=16 

와 보유 기간 매개 변수를 할 수

. 앱의 다양한 연령 버킷을 인쇄 할 수 있습니다.

java -XX:-PrintTenuringDistribution 
+0

이 힙을 파트로 분할하는 점은 무엇입니까? minor GC는 GC가 전체 힙 대신 젊은 세대에있는 객체에 대해서만 그래프를 통과한다는 것을 의미합니까? – Malachiasz

+1

마이너 GC는 젊은 세대에서만 발생합니다. http://www.oracle.com/을 참조하십시오.com/technetwork/java/javase/gc-tuning-6-140523.html을 참조하십시오. –

1

이것은 개체의 기대 여명이 나이가 들면서 올라가는 관찰에 근거합니다. 따라서 특정 연령에 도달하면 개체를 자주 수집하지 않는 풀로 이동하는 것이 좋습니다.

이것은 프로그램에서 메모리를 사용하는 방식의 기본 속성이 아닙니다. 당신은 오랜 시간 동안 (그리고 모든 객체에 대해 같은 길이의 시간 동안) 모든 객체를 유지하는 병리학 적 프로그램을 작성할 수 있습니다. 그러나 이것은 우발적으로 발생하지 않는 경향이 있습니다.

+1

내가 휘젓는 코드의 대부분은 병적이라고 할 수 있습니다. 그 중 일부는 완전히 반사회적입니다 :-) – paxdiablo

+0

"병리학 적 프로그램을 작성할 수는 있지만 우연히 발생하지는 않습니다." 많은 프로그램이 세대 별 GC에 병적이라고 생각합니다. 대기열, 해시 테이블 및 캐시가 모두 있습니다. –

+0

@JonHarrop : 대기열이 불편 해하면서 해시 테이블의 내용이 얼마나 오래 지속되는지에 대한 해설이없고 캐시 객체 수명이 일반적으로 크게 비뚤어지는 인기에 묶입니다. 병적으로 행동하도록 이러한 데이터 구조를 강요 할 수는 있지만 본질적으로 그렇습니다. –

4

이것은 세대 별 가비지 수집입니다. 요즘 꽤 널리 쓰이고 있어요. 자세한 내용은 (wiki)을 참조하십시오.

기본적으로 GC는 새 개체가 이전 개체보다 연결할 수 없게 될 가능성이 높다고 가정합니다.

+1

이 답변에는 질문에 대답하기위한 의미있는 정보가 없습니다. – stackoverflowuser2010

+0

이 여섯 살짜리 대답에 대한 귀하의 의견에 감사드립니다. 그 사람이 찾고 있던 흐릿한 개념에 대한 정식 이름을 제공하고 찾고자하는 것을 정확하게 설명하는 매우 자세한 참조로 연결했습니다. –

1

JVM (일반적으로)은 세대 별 가비지 수집기를 사용합니다. 이 종류의 수집기는 거기에있는 객체의 나이에 따라 힙 메모리를 여러 풀로 구분합니다. 여기에있는 추론은 대부분의 객체가 수명이 짧다는 관찰을 기반으로합니다. 따라서 "젊은"객체를 사용하여 메모리 영역에서 가비지 수집을 수행 할 경우 이전 버전에서 가비지 수집을 수행하는 것보다 상대적으로 많은 메모리를 회수 할 수 있습니다 " 사물.

핫스팟 JVM에서 새 객체는 이덴 (Eden) 영역에 할당됩니다. 이 영역이 채워지면 JVM이 에덴 영역을 스윕합니다 (그리 크지 않기 때문에 시간이 오래 걸리지 않습니다). 아직 살아있는 객체는 Survivor 영역으로 이동되고 나머지 객체는 폐기되어 다음 세대를 위해 Eden이 해제됩니다. Eden 컬렉션이 충분하지 않으면 가비지 컬렉터가 이전 세대로 이동합니다 (더 많은 작업이 필요함).

+0

핫스팟 JVM은 이전 객체에서 참조되는 새로운 객체를 감지하기 위해 어떤 수단을 사용합니까? Eden을 마지막으로 휩쓸고 난 이후로 수정 된 오래된 객체에 태그를 붙이려면 쓰기 울타리를 사용합니까? .net GC에 대한 나의 이해에 따르면, 그것은 마지막 gen-0 콜렉션 이후로 수정되지 않은 오브젝트가 gen-0 오브젝트에 대한 직접 또는 간접 참조를 보유 할 수 없으며, 마지막 gen-1 모음이 gen0 또는 gen1 객체에 대한 직접 또는 간접 참조를 보유 할 수 없기 때문에 수정되었습니다. – supercat

4

"대부분의 물체가 젊은 나이에 죽는다"는 현상이 있습니다. 많은 개체는 메서드 내에서 만들어지며 결코 필드에 저장되지 않습니다. 따라서 메소드가 종료되자 마자 이러한 객체는 "죽습니다"따라서 다음 수집주기에서 수집 할 수 있습니다.

public String concatenate(int[] arr) { 
    StringBuilder sb = new StringBuilder(); 
    for(int i = 0; i < arr.length; ++i) 
    sb.append(i > 0 ? "," : "").append(arr[i]); 
    return sb.toString(); 
} 

sb 객체가 쓰레기가 될 것이다 가능한 한 빨리 메소드가 리턴 : 여기

은 예입니다.

개체 공간을 2 개 이상의 연령 기반 영역으로 분리하면 GC가 전체 힙을 자주 검색하는 대신 자주 GC (젊은 개체 영역) 만 스캔하므로, 분명히 전체 힙 스캔보다 훨씬 적은 시간이 걸립니다. 오래된 개체 영역은 자주 스캔되지 않습니다.

1

모든 GC가 정상적으로 작동합니다. 기본 아이디어는 GC를 실행할 때마다 확인해야하는 개체의 양을 줄이려고 시도하는 것입니다. 이는 꽤 비싼 작업이기 때문입니다. 따라서 수백만 개의 개체가 있지만 몇 개만 확인해야하는 경우 모든 개체를 확인하는 것보다 낫습니다. 또한 GC의 기능이 사용자의 손에 달려 있습니다. 임시 객체 (더 이상 다른 사용자가 접근 할 수없는 객체)는 GC 실행 중에 비용이 들지 않습니다 (이제는 finalize() 메소드를 무시하십시오). 생존하는 객체 만 CPU 시간을 소비합니다. 다음으로, 많은 대상이 수명이 짧다는 관찰이 있습니다.

따라서 개체는 작은 공간 ("Eden"또는 "young gen")에 만들어집니다. 잠시 후 도달 할 수있는 모든 객체는이 공간에서 복사 (= 값 비싼)되고 공백은 공백으로 선언됩니다 (Java는 도달 할 수없는 모든 객체를 사실상 잊어 버리기 때문에 비용이 들지 않습니다. 복사해야합니다.) 시간이 지남에 따라 수명이 긴 오브젝트는 "이전"공간으로 이동되고 이전 공간은 GC 오버 헤드를 줄이기 위해 자주 스윕되지 않습니다 (예 : 모든 N 실행, GC는 에덴 공간 대신 ​​이전 공간을 실행합니다).

그냥 비교 : C/C++에서 개체를 할당하는 경우 각각에 대해 free()과 소멸자를 호출해야합니다. 이것이 GC가 전통적인 수동 메모리 관리보다 더 빠른 이유 중 하나입니다.

물론 이것은 다소 단순 해 보입니다. 오늘날 GC에 대한 작업은 컴파일러 디자인 수준 (즉 소수의 사람들 만 수행)에서 이루어집니다. GC는 모든 과정을 효율적이고 눈에 잘 띄지 않게하기 위해 모든 종류의 기법을 사용합니다. 몇 가지 지침은 Wikipedia article을 참조하십시오.

2

젊은 개체는 특수 영역 ("젊은 세대")에 할당되어 있기 때문에 더 효율적으로 관리됩니다 (어린 개체에 대한 액세스도 더 빠릅니다). 이 특수 영역은 "한 번에"수집되므로 (모든 스레드가 중지 된 상태로) 콜렉터 나 응용 프로그램 코드가 다른 프로세스와의 동시 액세스를 처리해야하므로 더 효율적입니다.

"효율적인 영역"을 수집 할 때 "세계"가 중지된다는 점에서 절충됩니다. 이로 인해 눈에 띄는 일시 중지가 발생할 수 있습니다. JVM은 효율적인 영역을 충분히 작게 유지하여 일시 중지 시간을 낮게 유지합니다. 즉, 효율적으로 관리되는 영역이있는 경우 해당 영역은 작아야합니다.

많은 프로그램 및 프로그래밍 언어에 적용 할 수있는 매우 일반적인 경험적 방법은 많은 개체가 매우 수명이 짧으며 대부분의 쓰기 액세스가 최근에 생성 된 개체 (어린 개체)에서 발생한다는 것입니다. 그런 식으로 작동하지 않는 응용 프로그램 코드를 작성할 수도 있지만 이러한 추론은 "대부분의 응용 프로그램"에서 "거의 사실"입니다. 따라서 효율적으로 관리되는 영역에 젊은 객체를 저장하는 것이 좋습니다. JVM GC가하는 것은 무엇이며, 따라서 효율적인 영역을 "젊은 세대"라고합니다.

전체 메모리가 "효율적으로"처리되는 시스템이 있습니다. GC를 실행해야 응용 프로그램이 몇 초 동안 "정지"됩니다. 이것은 장시간의 계산에서는 무해하지만 상호 작용에 해로울 수 있습니다. 이것이 최신 GC 기반 프로그래밍 환경이 제한된 젊은 세대의 세대 GC를 사용하는 이유입니다.

+0

"여기에서"효율적인 영역 "을 수집 할 때"세계 "가 중지된다는 것이 절충됩니다. 젊은 세대를 모으기 위해 세상을 막지는 않을 것입니다. –

+0

"입니다. 이것이 바로 최신 GC 기반 프로그래밍 환경이 제한된 젊은 세대 세대 GC를 사용하는 이유입니다." 세대 별 GC는 대기 시간을 크게 늘리는 데 도움이되지 않습니다. 처리량은 세대 별 GC를 선택하는 진정한 동기 였지만 이제는 마크 영역 수집기로 인해 어려움을 겪고 있습니다. –

8

(더 일반적인 GC에 대한 위의 설명을 참조하십시오. 이것은 이전의 것보다 GC가 더 저렴한 이유는 무엇입니까?)

eden을 더 빨리 지울 수있는 이유는 간단합니다. 알고리즘은 전체 힙의 라이브 개체 수에 비례하지 않고 에덴 공간에서 GC를 견딜 수있는 개체 수에 비례합니다. 예를 들어 : 99 %의 개체 사멸률 (즉, 개체의 99 %가 비정상적이지 않은 GC에서 살아남지 못함)이 99 %라면 그 1 % 만보고 복사하면됩니다. "오래된"GC의 경우, 전체 힙의 모든 활성 객체를 표시/제거해야합니다. 그것은 훨씬 더 비쌉니다.

+0

지금까지 최선의 대답. 참조가없는 개체는 자동으로 수집됩니다. –

관련 문제