[java] OutOfMemoryError를 발생시키기에 메모리가 부족하면 어떻게됩니까?

모든 객체에는 힙 메모리가 필요하고 스택의 모든 기본 / 참조에는 스택 메모리가 필요하다는 것을 알고 있습니다.

힙에 객체를 만들려고하는데 메모리가 충분하지 않으면 JVM 이 힙에 java.lang.OutOfMemoryError 를 생성 하여 나에게 던집니다.

따라서 암시 적으로 이것은 시작시 JVM이 예약 한 일부 메모리가 있음을 의미합니다.

이 예약 된 메모리가 모두 사용되면 (아래에서 논의 내용을 읽음) JVM에 java.lang.OutOfMemoryError 인스턴스를 작성하기에 충분한 메모리가 힙에없는 경우 어떻게됩니까?

그냥 걸어 요? 아니면 OOM 인스턴스에 null대한 기억이 없기 때문에 그는 나를 던질 것 new입니까?

try {
    Object o = new Object();
    // and operations which require memory (well.. that's like everything)
} catch (java.lang.OutOfMemoryError e) {
    // JVM had insufficient memory to create an instance of java.lang.OutOfMemoryError to throw to us
    // what next? hangs here, stuck forever?
    // or would the machine decide to throw us a "null" ? (since it doesn't have memory to throw us anything more useful than a null)
    e.printStackTrace(); // e.printStackTrace() requires memory too.. =X
}

==

JVM이 충분한 메모리를 예약 할 수없는 이유는 무엇입니까?

예약 된 메모리 양에 관계없이 JVM에 해당 메모리를 “재 확보”할 방법이없는 경우 해당 메모리를 계속 사용할 수 있습니다.

try {
    Object o = new Object();
} catch (java.lang.OutOfMemoryError e) {
    // JVM had 100 units of "spare memory". 1 is used to create this OOM.
    try {
        e.printStackTrace();
    } catch (java.lang.OutOfMemoryError e2) {
        // JVM had 99 units of "spare memory". 1 is used to create this OOM.
        try {
            e.printStackTrace();
        } catch (java.lang.OutOfMemoryError e3) {
            // JVM had 98 units of "spare memory". 1 is used to create this OOM.
            try {
                e.printStackTrace();
            } catch (java.lang.OutOfMemoryError e4) {
                // JVM had 97 units of "spare memory". 1 is used to create this OOM.
                try {
                    e.printStackTrace();
                } catch (java.lang.OutOfMemoryError e5) {
                    // JVM had 96 units of "spare memory". 1 is used to create this OOM.
                    try {
                        e.printStackTrace();
                    } catch (java.lang.OutOfMemoryError e6) {
                        // JVM had 95 units of "spare memory". 1 is used to create this OOM.
                        e.printStackTrace();
                        //........the JVM can't have infinite reserved memory, he's going to run out in the end
                    }
                }
            }
        }
    }
}

더 간결하게 :

private void OnOOM(java.lang.OutOfMemoryError e) {
    try {
        e.printStackTrace();
    } catch (java.lang.OutOfMemoryError e2) {
        OnOOM(e2);
    }
}



답변

JVM에는 실제로 메모리가 부족하지 않습니다. 힙 스택의 메모리 계산을 미리 수행합니다.

JVM구조, 3 장 , 섹션 3.5.2는 다음과 같이 말합니다.

  • Java 가상 머신 스택을 동적으로 확장 할 수 있고 확장을 시도했지만 확장에 영향을 줄 수있는 메모리가 부족하거나 새 스레드에 대한 초기 Java 가상 머신 스택을 작성하는 데 메모리가 부족한 경우 Java 가상 머신 기계를 던졌습니다 OutOfMemoryError.

들어 , 제 3.5.3.

  • 계산에 자동 스토리지 관리 시스템에서 사용할 수있는 것보다 많은 힙이 필요한 경우 JVM (Java Virtual Machine)은 OutOfMemoryError.

따라서 객체를 할당하기 전에 미리 계산을 수행합니다.


JVM이 Permanent Generation region (또는 PermSpace)이라는 메모리의 오브젝트에 메모리를 할당하려고합니다. JVM이 가비지 콜렉터를 호출하여 여유 공간을 할당 및 할당 한 후에도 할당에 실패하면가 처리됩니다 OutOfMemoryError. 예외조차도 메모리 공간이 필요하므로 오류가 무한대로 발생합니다.

더 읽을 거리. ? 또한 OutOfMemoryError다른 JVM 구조 에서 발생할 수 있습니다 .


답변

Graham Borland는 옳은 것 같습니다 : 적어도 JVM은 OutOfMemoryErrors를 재사용합니다. 이것을 테스트하기 위해 간단한 테스트 프로그램을 작성했습니다.

class OOMTest {
    private static void test (OutOfMemoryError o) {
        try {
            for (int n = 1; true; n += n) {
                int[] foo = new int[n];
            }
        } catch (OutOfMemoryError e) {
            if (e == o)
                System.out.println("Got the same OutOfMemoryError twice: " + e);
            else test(e);
        }
    }
    public static void main (String[] args) {
        test(null);
    }
}

실행하면 다음과 같은 출력이 생성됩니다.

$ javac OOMTest.java && java -Xmx10m OOMTest
Got the same OutOfMemoryError twice: java.lang.OutOfMemoryError: Java heap space

BTW, 내가 실행중인 JVM (Ubuntu 10.04에서)은 다음과 같습니다.

$ java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

편집 : 나는 경우 무슨 일이 일어날 지 볼려고 강제로 다음과 같은 프로그램을 사용하여 메모리에서 완전히 실행하는 JVM을 :

class OOMTest2 {
    private static void test (int n) {
        int[] foo;
        try {
            foo = new int[n];
            test(n * 2);
        }
        catch (OutOfMemoryError e) {
            test((n+1) / 2);
        }
    }
    public static void main (String[] args) {
        test(1);
    }
}

결과적으로 영원히 반복되는 것처럼 보입니다. 그러나 흥미롭게도 Ctrl+로 프로그램을 종료하려고하면 C작동하지 않지만 다음 메시지 만 제공합니다.

Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated


답변

대부분의 런타임 환경은 메모리 부족 상태를 처리하기에 충분한 메모리가 시작시 사전 할당되거나 예약됩니다. 제정신 JVM 구현이 대부분 그렇게 할 것이라고 생각합니다.


답변

마지막으로 Java에서 작업하고 디버거를 사용했을 때 힙 관리자는 시작시 JVM이 OutOfMemoryError 인스턴스를 할당했음을 보여주었습니다. 다시 말해, 프로그램이 메모리를 다 쓰지 않고 소비를 시작하기 전에 개체를 할당합니다.


답변

JVM 스펙, 3.5.2 장에서 :

Java 가상 머신 스택을 동적으로 확장 할 수 있고 확장을 시도했지만 확장에 영향을 줄 수있는 메모리가 부족하거나 새 스레드에 대한 초기 Java 가상 머신 스택을 작성하는 데 메모리가 부족한 경우 Java 가상 기계를 던졌습니다OutOfMemoryError .

모든 Java Virtual Machine은 OutOfMemoryError. 즉, OutOfMemoryError힙 공간이 없어도 인스턴스를 만들 거나 미리 만들 수 있어야합니다.

보장 할 필요는 없지만 메모리를 잡아서 멋진 스택 트레이스를 인쇄 할 수있는 충분한 공간이 남아 있습니다 …

덧셈

JVM에 둘 이상을 던져야하는 경우 힙 공간이 부족할 수 있음을 보여주는 코드를 추가했습니다 OutOfMemoryError. 그러나 이러한 구현은 위의 요구 사항을 위반합니다.

던져진 인스턴스 OutOfMemoryError가 고유하거나 필요시 생성 될 필요는 없습니다. JVM은 OutOfMemoryError시작 하는 동안 정확히 하나의 인스턴스를 준비 하고 힙 공간이 부족할 때마다 (일반 환경에서는 한 번)이를 던질 수 있습니다. 다시 말해, OutOfMemoryError우리가 본 사례 는 싱글 톤일 수 있습니다.


답변

재미있는 질문 :-). 다른 사람들은 이론적 측면에 대한 좋은 설명을 들었지만, 나는 그것을 시도하기로 결정했습니다. 이것은 Oracle JDK 1.6.0_26, Windows 7 64 비트에 있습니다.

테스트 설정

메모리를 소진하는 간단한 프로그램을 작성했습니다 (아래 참조).

이 프로그램은 static을 만들고 java.util.ListOOM이 throw 될 때까지 새로운 문자열을 채워 넣습니다. 그런 다음 그것을 잡고 끝없는 루프 (가난한 JVM …)로 채워 넣습니다.

검사 결과

출력에서 볼 수 있듯이 OOME이 처음 네 번 발생하면 스택 추적이 제공됩니다. 그 후, 후속 OOME 은 호출 된 java.lang.OutOfMemoryError: Java heap space경우 에만 인쇄 합니다 printStackTrace().

따라서 JVM은 가능한 경우 스택 추적을 인쇄하려고 노력하지만 메모리가 너무 빡빡하면 다른 답변에서 제안한 것처럼 추적을 생략합니다.

또한 흥미로운 것은 OOME의 해시 코드입니다. 처음 몇 OOME은 모두 다른 해시를가집니다. JVM이 스택 추적 생략을 시작하면 해시는 항상 동일합니다. 이것은 JVM이 가능한 한 새로운 (사전 할당 된?) OOME 인스턴스를 사용할 것을 제안하지만 푸시가 발생하면 아무것도 던지지 않고 동일한 인스턴스를 재사용합니다.

산출

참고 : 출력을보다 쉽게 ​​읽을 수 있도록 일부 스택 추적을 자릅니다 ( “[…]”).

iteration 0
iteration 100000
iteration 200000
iteration 300000
iteration 400000
iteration 500000
iteration 600000
iteration 700000
iteration 800000
iteration 900000
iteration 1000000
iteration 1100000
iteration 1200000
iteration 1300000
iteration 1400000
iteration 1500000
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 1069480624
Keep on trying...
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.ArrayList.ensureCapacity(Unknown Source)
    at java.util.ArrayList.add(Unknown Source)
    at testsl.Div.gobbleUpMemory(Div.java:23)
    at testsl.Div.exhaustMemory(Div.java:12)
    at testsl.Div.main(Div.java:7)
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
[...]
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 616699029
Keep on trying...
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
[...]
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 2136955031
Keep on trying...
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
[...]
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 1535562945
Keep on trying...
java.lang.OutOfMemoryError: Java heap space
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 1734048134
Keep on trying...
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 1734048134
Keep on trying...
java.lang.OutOfMemoryError: Java heap space
Ouch: java.lang.OutOfMemoryError: Java heap space; hash: 1734048134
Keep on trying...
[...]

프로그램

public class Div{
    static java.util.List<String> list = new java.util.ArrayList<String>();

    public static void main(String[] args) {
        exhaustMemory();
    }

    private static void exhaustMemory() {
        try {
            gobbleUpMemory();
        } catch (OutOfMemoryError e) {
            System.out.println("Ouch: " + e+"; hash: "+e.hashCode());
            e.printStackTrace();
            System.out.println("Keep on trying...");
            exhaustMemory();
        }
    }

    private static void gobbleUpMemory() {
        for (int i = 0; i < 10000000; i++) {
            list.add(new String("some random long string; use constructor to force new instance"));
            if (i % 10000000== 0) {
                System.out.println("iteration "+i);
            }
        }

    }
}


답변

JVM은 메모리가 부족하기 전에 예외를 throw하기에 충분한 메모리가 있는지 확인합니다.