2009-07-08 2 views
3

는 OutOfMemoryError가 생성됩니다. 이 코드는 하나의 변수 할당 (첫 번째 배열에서 사용 된 스택 프레임을 다시 작성하고 가비지 수집을 위해 배열을 만들 수 있음)로 작동하도록 할 수 있습니다. 이 퍼즐은 here이라고 설명했습니다.자바 메모리 퍼즐

{ 
    byte[] data1 = new byte[size]; 
} 
int i = 0; 
byte[] data2 = new byte[size]; 

질문 : 왜 다음 코드가 여전히 작동하지 않습니까?

Object o = new Object(); 
synchronized (o) { 
    byte[] data1 = new byte[size]; 
} 
int i = 0; 
byte[] data2 = new byte[size]; 

그리고 다음 작품 :

Object o = new Object(); 
synchronized (o) { 
    byte[] data1 = new byte[size]; 
} 
int i = 0; 
synchronized (o) { 
    byte[] data2 = new byte[size]; 
} 
+1

yuck ...이 동작은 가비지 수집기의 구현에 전적으로 의존하지 않습니까? 이 동작이 결정적이지 않은 것처럼 보입니다 (즉, JLS로 지정되어 있지 않다). 내가 잘못? – Tom

+2

JLS에서는 GC를 전혀 수행하지 않고 구현할 수 있습니다 (GC에 대한 계약은 어떻게 지정하겠습니까?). –

+1

기사에 링크 된 기사를 보면 GC에 의존하지 않는 것 같지만 생성 된 바이트 코드 (스택에 배열 참조가 너무 길게 유지됨)가 있습니다. 한편이 기사에서는 BEA와 IBM VM 또는 Sun의 실험용 새 가비지 수집기에서도 오류가 발생하지 않는다고 설명합니다. 혼란스러운 ... – Thilo

답변

6

내 내기 synchronized 프레임에 요소를 추가하는 것입니다 할 수 없었다. synchronized은 로컬/필드가 변경되는 경우에도 잠긴 동일한 개체의 잠금을 해제해야합니다.

synchronized 코드는 다음과 같을 것이다 :

Object $sync = o; 
$sync.lock(); 
try { 
    byte[] data1 = new byte[size]; 
} finally { 
    $sync.unlock(); 
} 

그래서 코드의 마지막 샘플을 복용 :

Object o = new Object();   // Slot 0. 
synchronized (o) {     // Slot 1. 
    byte[] data1 = new byte[size]; // Slot 2. 
}         
int i = 0;       // Slot 1. 
synchronized (o) {     // Slot 2. (clobbers data1, was slot 1) 
    byte[] data2 = new byte[size]; // Slot 3. 
} 
+0

그것은 그렇게 보인다. public static void main (String [] args) throws InterruptedException { synchronized (새 Object()) { byte [] data1 = 새 바이트 [크기]; } int i = 0; int k = 0; byte [] data2 = 새 바이트 [크기]; } 매우 잘 작동합니다. 이 코드를 디스 어셈블하면 "k"변수가 "data1"의 스택 프레임을 덮어 씁니다. –

+0

말문이 없습니다. 좋은 물건. –

-1

당신은 당신의 인스턴스화하기 전에 수집하는 GC에 의존하는거야?

당신은 i에 의해 사방에서 공격받을 슬롯을 이동하지 data1 원인

Object o = new Object(); 
byte[] data1 = new byte[size]; 
GC.Collect() 
byte[] data2 = new byte[size]; 
+1

GC는 OutOfMemoryError를 던지기 전에 항상 수집합니다 (http://java.sun.com/javase/6/docs/api/java/lang/OutOfMemoryError.html). 믿을만한 행동. 코드가 범위를 벗어나지 않고 추가 최적화가 필요하고 데이터 1을 안전하게 다시 확보 할 수 있으므로 데이터 1을 신뢰할 수있는 코드로 수집 할 필요가 없습니다. – Mnementh

+0

그는 그렇게해서는 안됩니다. JVM은 OutOfMemoryError를 던지기 전에 메모리를 확보하기 위해 열심히 노력해야하므로 가비지 컬렉터가 자동으로 호출되며 전체 스윕 모드에서도 호출됩니다. – Thilo

+0

* GC는 OutOfMemoryError를 throw하기 전에 항상 java.sun.com/javase/6/... 문서에 설명 된대로 수집합니다. 따라서 적어도 신뢰할 수있는 동작입니다. * 흥미 롭습니다. 그러나 가비지 수집기가 사용되지 않는 모든 객체를 한 번에 수집한다는 보장을하지 않기 때문에 여전히 결정적이지 않습니다. http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html#par_gc.oom에서 설명합니다. – cygil

0

퍼즐 재미 있지만, 생각하지 않는 실용주의 프로그래머 더 이상 (또는 더 중요하게) 쓰레기 수거의 더 분명한 측면에 의존한다면, 더 이상 필요하지 않게되자 마자 data1 = null을 설정하면 문제를 해결할 수 있습니까? 그렇다면 이상하게 동기화 된 블럭과 더미 변수 마술을하고 싶습니다.

물론 배열이 범위를 벗어나 자마자 메모리가 해제되지 않는다는 것은 슬픈 일입니다. 사람들이 희망하는 것은 this thread입니다.

이것은 JVM에서 수정해야합니다.

+0

물론 실용적인 프로그래머는 무지로 인해 생산에 문제가 발생할 때까지는 괜찮습니다. –

+0

이와 같은 세부 정보에 얽히지 않기를 거부하면 무지로 간주됩니다. 이것은 JVM 전문가를 제외하고는 아무도 알지 못하는 드문 경우입니다. – Thilo

0

이 모든 동작은 구현에 따라 다릅니다. 가비지 수집기는 프로그램의 동기화 동작과 관련이없는 자체 비동기 스레드에서 실행됩니다. data1에 의해 참조되는 배열이 가비지 수집 될 때 당신은 단순히 알 수 없습니다. 범위를 벗어난 "합리적인"시간 내에 만 이루어지기를 바랄뿐입니다.

프로그램에서 메모리 부족 문제가 발생할 경우 System.gc()를 사용하여 가비지 수집주기를 명시 적으로 트리거 할 수 있습니다. 그러나 이것으로도 데이터를 할당 할 때 충분한 메모리를 사용할 수 있다고 보장 할 수는 없습니다 2. System.gc()를 호출하면 이제 런타임에서 가비지 수집주기를 원한다는 힌트 일뿐입니다.

Java에서 메모리 할당 및 할당 취소는 비 결정적입니다. 가비지 수집기는 실행될 때 실행되며 프로그램 수준에서 실행할 수 없습니다. 게시 된 코드 스 니펫 간에는 관련성이 없습니다. gc 동작은 비 결정적이며 트리거되는 정확한 순간은 구현 및 시스템에 따라 다르기 때문에때로는 이것이 응용 프로그램의 문제입니다 - 예를 들어 OS이거나 메모리가 제한된 임베디드 장치에서 실행되는 경우 - C++ 또는 메모리 관리가 결정적인 다른 언어로 코딩해야합니다. 대부분의 경우 가비지 수집기가 합리적으로 합리적인 방식으로 작동하고 대부분의 용도에 충분하다는 것을 신뢰합니다.하지만 알다시피 문제를 일으키는 고안된 코드를 만들 수는 있습니다.

업데이트 : 당혹 스럽네요. 몇 명의 다른 주석가가 나에게 상기시켜 줬 듯이, jvm이 OutOfMemory 오류를 throw하기 전에 가비지 콜렉션주기가 명시 적으로 트리거됩니다. 그러나이 동작은 여전히 ​​비 결정적입니다. this link에서 설명한 것처럼 jvm은 하나의 가비지 수집주기에서 모든 죽은 객체가 감지되는 것을 보장하지 않습니다.

+0

하지만 OutOfMemoryError를 던지기 전에 JVM이 전체 (백그라운드가 아닌) 가비지 콜렉션을 강제 실행하지 않습니까? – Thilo