2012-01-11 2 views
11

WeakHashMap에서 몇 가지 문제가 발생했습니다.전체 GC 동안 WeakHashMap이 지워졌습니까?

List<byte[]> list = new ArrayList<byte[]>(); 

Map<String, Calendar> map = new WeakHashMap<String, Calendar>(); 
String anObject = new String("string 1"); 
String anOtherObject = new String("string 2"); 

map.put(anObject, Calendar.getInstance()); 
map.put(anOtherObject, Calendar.getInstance()); 
// In order to test if the weakHashMap works, i remove the StrongReference in this object 
anObject = null; 
int i = 0; 
while (map.size() == 2) { 
    byte[] tab = new byte[10000]; 
    System.out.println("iteration " + i++ + "map size :" + map.size()); 
    list.add(tab); 
} 
System.out.println("Map size " + map.size()); 

이 코드는 작동합니다

이 샘플 코드를 살펴 보자. 루프 내에서 객체를 생성합니다. 마이너 GC가 발생하면 1360 번째 반복에서 맵 크기가 1과 같습니다. 다 괜찮아. 내가이 줄을 주석 이제

다음 mapSize는 그러나 26XXX 회 반복 항상 2와 동일하기 때문에 내가 OutOfMemoryError가있을 것으로 예상

//anObject = null; 

는 전체 GC가 발생하고지도의 크기는 같다 이유는 모르겠다.

두 개체에 대한 강력한 참조가 있으므로지도를 지워서는 안된다고 생각했습니다.

루프가 아직 실행 중에 컴파일러, 코드 분석 anObjectanOtherObject 루프 이후에 사용되지 않는 것으로보고, 로컬 변수 테이블에서 제거 또는 null로 설정하는 적시
+0

테스트가 올바르지 않다고 생각합니다. 'while (map.size() == 2) {'를 while (map.size()> 0) {'로 변경하면 두 테스트는지도가 비어있을 때까지 끝날 것입니다. = null' 또는 아닌지. BTW, 나는 이미 그것을 시도했다. – donnior

+0

끝에'anObject'와'anOtherObject'를 출력하십시오. 컴파일러는 더 이상 사용하지 않고 이전에 제거 할 수 있음을 확인합니다. –

답변

10

. 이를 OSR 컴파일이라고합니다.

나중에 GC는 강력한 참조가 남아 있지 않기 때문에 문자열을 수집합니다.

루프 후 anObject을 사용한 경우에도 OutOfMemoryError이 표시됩니다.

업데이트 : 당신은 내 블로그에 OSR compilation에 대한보다 자세한 설명을 확인할 수 있습니다. 굴착

+0

저는 여러분이 정확히 맞다고 생각합니다. 그러나 이것은 잠재적으로 파괴적인 JIT 최적화가 아니십니까? 'anObject'가 파이널 라이저를 가지고 있고 그 참조가 사라지기 전에 GC가된다면 finalizer는 의도되기 전에 잠재적으로 실행될 것입니다. – berry120

+0

무엇이 깨질 수 있습니까? 파이널 라이저가 실행되면 더 이상 강력한 참조가 존재하지 않습니다. – Joni

+0

그것은 예상보다 빨리 finalizer를 실행할 수 있다는 의미에서 깨질 수 있습니다. 하드 레퍼런스가 실제로 범위를 벗어나기 전에. – berry120

7

비트이 명시 적 JLS에 포함되어 있는지 알 섹션 12.6.1 : 프로그램의

최적화 변환을 설계 할 수있는 그 이하로 도달 가능한 오브젝트의 수를 줄일 순진하게 도달 할 수있는 것으로 간주됩니다. 예를 들어, 컴파일러 나 코드 생성기는 더 이상 null로 사용되지 않는 변수 나 매개 변수를 설정하여 이러한 개체의 저장소를 잠재적으로보다 쉽게 ​​회수 할 수 있습니다.

(굵은 글꼴은 내 추가되었습니다.)

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.6.1

그래서 본질적으로, JIT은 원하는 때마다 그들이 사용하지 않을거야 해결할 수 있다면 강한 참조를 제거 할 수있다 다시 - 정확히 여기서 일어나는 일입니다.

이것은 위대한 질문이지만 개체가 범위에서 강력한 참조를 갖고있는 것처럼 쉽게 나타낼 수있는 위대한 수수께끼를 만듭니다. 반드시 가비지 수집되지 않았 음을 의미하지는 않습니다. 이것에이어서, finalizer가 언제 실행되는지에 대해 명시 적으로 보장 할 수 없다는 것을 의미합니다. 이것은 객체가 여전히 범위 내에있는 것처럼 보이는 경우 일 수도 있습니다!

예 :

List<byte[]> list = new ArrayList<byte[]>(); 

Object thing = new Object() { 
    protected void finalize() { 
     System.out.println("here"); 
    } 
}; 
WeakReference<Object> ref = new WeakReference<Object>(thing); 

while(ref.get()!=null) { 
    list.add(new byte[10000]); 
} 
System.out.println("bam"); 

은 상기 오브젝트가 최종 얻고 GC'd 먼저 thing에 대한 참조가 여전히 존재하더라도 보여주는 간단한 예이다

7
(여기가 BAM 다음, 인쇄된다.)

의 조니 살로 넨berry120의 우수 답변에 작은 것을 추가하기 만하면됩니다. JIT는 실제로 "변수 제거"에 대한 책임이 있으며 단순히 -Djava.compiler=NONE으로 해제한다는 것을 알 수 있습니다. 일단 당신이 그것을 끄면, 당신은 오메를 얻습니다.

후드에서 어떤 일이 발생했는지 알고 싶다면 XX:+PrintCompilation 옵션에 JIT 활동이 표시됩니다.

1  java.lang.String::hashCode (64 bytes) 
2  java.lang.String::charAt (33 bytes) 
3  java.lang.String::indexOf (151 bytes) 
4  java.util.ArrayList::add (29 bytes) 
5  java.util.ArrayList::ensureCapacity (58 bytes) 
6 ! java.lang.ref.ReferenceQueue::poll (28 bytes) 
7  java.util.WeakHashMap::expungeStaleEntries (125 bytes) 
8  java.util.WeakHashMap::size (18 bytes) 
1%  WeakHM::main @ 63 (126 bytes) 
Map size 0 

합니다 (@ 플래그) 마지막으로 컴파일 컴파일 (스택 교체에)는 OSR (자세한 내용은 https://gist.github.com/1165804#file_notes.md 확인)입니다 : 출력이 우리가 얻을 질문의 코드와 함께 사용하면 다음과 같다. 간단히 말해, 메소드가 실행되는 동안 VM이 메소드를 대체 할 수있게하며, 루프에 고정 된 Java 메소드의 성능을 향상시키는 데 사용됩니다. 이 컴파일이 트리거 된 후 JIT는 더 이상 사용되지 않는 변수를 제거합니다.

관련 문제