2014-02-06 4 views
2
내가 캐시 구현하는 SoftReference를 테스트입니다

에서 수집 나는 이상한 행동을 발견 : 나는하는 SoftReference를 통해 그래프 개체의 이름을 설정하는 setName(String name) 방법이하는 SoftReference는 <String>없는 쓰레기 때 메모리

을 :

(garbagedData는 ReferenceQueue이며이 특정 문제에서는 중요하지 않음).

주 스레드에서 graph.setName("name");을 호출하고 OutOfMemory 오류가 발생하면 참조가 가리키는 값이 garbaged가 아니지만 graph.setName(new String("name"))을 호출하면 오류가 발생합니다.

이클립스 메모리 분석기로 힙 내용을 볼 수 있으며 두 경우 모두 소프트 힙보다 다른 참조 체인이 없습니다.

누군가가이 이상한 행동에 대한 설명을 갖고 있다면 나는 관심이있다.

+0

메모리 제한 캐시는 매우 나쁜 생각입니다. 오래된 항목은 수명이 오래 지속됩니다. – kdgregory

+0

@kdgregory : 나는 그것이 나쁜 생각이지만, 대부분 당신은 전혀 통제 할 수 없다는 데 동의합니다. 그러나 "유용 수명"이 무엇인지 알 수없고 다른 캐시의 균형을 유지할 방법이 없습니다 (예 : "cache-1 제한을 1000 개 항목으로 설정해야합니까?"와 같은 것을 결정해야합니다. 캐시 -2는 함께 100MB 이상을 사용합니다. "). – maaartinus

+0

내 경우에는 "유용한 생명"이 가장 길다. 그러나 강력한 참조를 사용하면 메모리 부족이 발생할 수 있습니다. @kdgregory : 필요할 때마다 gc가 개체를 삭제할 수 있으면 개체를 메모리에 보관하는 것이 나쁜 일입니까? @ maaartinus : 참조를 수동으로 null로 설정하고 캐시 크기를 제한 할 수 있습니까? – zelus

답변

2

매우 간단합니다. 소스 코드에 나타나는 문자열은 intern입니다. 즉, 특수 풀에 보관되며 메소드 코드에서 참조하는대로 수집 할 기회가 없습니다. setName이 다시 호출 될 때 필요하기 때문에 분명히 쓰레기가 아닙니다.

new String("name")의 상황은 원본 String의 사본을 생성하므로 매우 다릅니다. 사실 this change부터 String.substring까지이 생성자를 사용할 필요가 없습니다.

Eclipse 메모리 분석기는 바이트 코드에 포함 된 참조를 표시하지 않는다고 생각합니다.

+0

고마워, 나는 인턴 문자열과이 특별한 수영장에 대해 몰랐다. 나는 그런 종류의 최적화를 의심했다. 'String.substring'에 대한 변경에 대해서, 나는 단지 생성자 대신에'substring (0)'을 호출해야한다는 것을 의미합니까? 그것은 나를 위해 매우 명시 적으로 보이지 않습니다. – zelus

+0

@zelus : 아니요.이 변경 이전에는 각 문자열에 공유 가능한 char []과 포인터로 'offset'과 'length'가있었습니다. 그래서'String s = aVeryLongString.substring (0, 1)'은 매우 빠른 연산 이었지만 거대한'char []'을 가리키는 객체가 있습니다. 이것을 막기 위해 배열의 필요한 부분을 복사 한'new String (s)'을 사용할 수 있습니다. 변경 후,'substring'은 복사본을 수행하므로 생성자를 더 이상 사용하지 않습니다. 'substring (0)'이나 다른 것으로 대체해서는 안됩니다. 전혀 필요 없다. – maaartinus

0

String 객체에 대한 SoftReference를 보유하고 있습니다. String 객체의 경우 JVM은 String 객체와 다르게 String 리터럴을 관리합니다.

String a = new String("test"); 
String b = new String("test"); 

이 예제에서 인스턴스 a 및 b는 다른 개체에 대한 참조입니다.

String a = "test"; 
String b = "test"; 

여기서 a와 b는 모두 동일한 문자열 리터럴에 대한 참조입니다. graph.setName ("name")을 호출하면 string literal에 대한 참조가 작성됩니다. 문자열 리터럴을 참조하는 String 객체는 일반적으로 가비지 수집 대상으로 간주되지 않습니다. 이렇게하면 String 객체가 가비지 수집되지 않습니다.

graph.setName (new String ("name"))을 호출하면 새로운 String 객체에 대한 SoftReference가 생성됩니다. 참조가 새로 생성 된 객체이고 문자열 리터럴이 아니기 때문에 가비지 수집 될 수 있습니다.