source

메모리가 부족하여 Out Of Memory Error를 발생시킬 수 없는 경우 어떻게 됩니까?

factcode 2022. 8. 13. 12:06
반응형

메모리가 부족하여 Out Of Memory Error를 발생시킬 수 없는 경우 어떻게 됩니까?

모든 오브젝트에는 힙메모리가 필요하고 스택상의 모든 프리미티브/레퍼런스에는 스택메모리가 필요하다는 것을 알고 있습니다.

힙에 개체를 생성하려고 하는데 이를 위한 메모리가 부족하면 JVM이 java.lang을 만듭니다.히프에 Out Of Memory Error가 있어 그것을 나에게 던집니다.

따라서 암묵적으로 이는 시작 시 JVM에 의해 예약된 메모리가 있음을 의미합니다.

이 예약된 메모리가 모두 사용되었을 때(확실히 다 사용되었을 경우, 아래의 설명 참조), JVM에 java.lang의 인스턴스를 생성하기에 충분한 메모리가 없는 경우.Out Of Memory Error?

? a a me me me me me me me me me ?null이 없기 때문에newOOM의 ?

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 가상 머신은 가상 머신 스택을 슬로우합니다.OutOfMemoryError.

히프의 경우 섹션 3.5.3.

  • 관리 할 수 은 Java를 .OutOfMemoryError.

따라서 오브젝트를 할당하기 전에 미리 계산을 합니다.


JVM Permanent Generation(Perm Space)은 Perm Space를 사용합니다.실패했을 경우(해 빈에도), 「」(JVM)이 「」(JVM)을 슬로우 .OutOfMemoryError예외에도 메모리 공간이 필요하기 때문에 오류가 무한정 발생합니다.

읽어볼래?더 나아가,OutOfMemoryError다른 JVM 구조에서 발생할 수 있습니다.

Graham Borland의 말이 맞는 것 같습니다.적어도 JVM은 Out Of Memory Errors를 재사용하고 있는 것 같습니다.이를 테스트하기 위해 간단한 테스트 프로그램을 작성했습니다.

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

참고로 Ubuntu 10.04에서 실행 중인 JVM은 다음과 같습니다.

$ 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);
    }
}

알고 보니 영원히 순환하는 것 같아요.그러나 이상하게도 +로 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이 Out Of Memory Error 인스턴스를 할당한 것으로 나타났습니다.즉, 프로그램이 메모리 부족은커녕 메모리 소비를 시작하기 전에 개체를 할당합니다.

JVM 사양, 3.5.2장 참조:

Java 가상 시스템 스택을 동적으로 확장할 수 있고 확장을 시도할 수 있지만 확장에 필요한 메모리가 부족하거나 메모리가 부족하여 새 스레드에 사용할 초기 Java 가상 시스템 스택을 생성할 수 없는 경우 Java 가상 시스템이 를 슬로우합니다.

Virtual Machine을 .OutOfMemoryError '를 수 합니다.OutOfMemoryError(또는 미리 생성해 둔 상태) 힙 공간이 남아 있지 않아도 됩니다.

보증할 필요는 없지만, 메모리 용량이 충분하여 스택 트레이스를 인쇄할 수 있습니다.

추가

의 "JVM"을 야 하는 수 있음을 했습니다.OutOfMemoryError다만, 이러한 실시는 상기의 요건을 위반하게 됩니다.

「」의 던져진 .OutOfMemoryError이치노은 JVM의 할 수 .OutOfMemoryError시작 시 힙 공간이 부족할 때마다(일반 환경에서 한 번) 던집니다. 예를 , of음음음, 음음, 음음, 음 of이다.OutOfMemoryError독신자일 수 있다는 걸 알 수 있어요

흥미로운 질문:-)다른 사람들이 이론적 측면에 대해 잘 설명해 주었지만, 나는 그것을 시도해 보기로 했다.이는 Oracle JDK 1.6.0_26, Windows 7 64비트에 있습니다.

테스트 셋업

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

뿐입니다.java.util.ListOOM이 터질 때까지 새로운 스트링을 계속 채워줍니다.그런 다음 이를 포착하고 끝없는 루프(불량 JVM...)로 계속 채워집니다.

시험결과

인쇄만 합니다.java.lang.OutOfMemoryError: Java heap spaceprintStackTrace()출됩니니다다

JVM은 가능한 한 스택 트레이스를 인쇄하려고 하지만 메모리가 너무 꽉 차면 다른 답변과 마찬가지로 트레이스를 생략합니다.

또한 흥미로운 것은 OOME의 해시 코드입니다. 처음 몇 개의 OOME은 모두 다른 해시를 가지고 있습니다.JVM이 스택트레이스를 생략하기 시작하면 해시는 항상 동일합니다.이는 JVM이 새로 고침(사전 할당됨)을 사용함을 나타냅니다.OOE 인스턴스는 가능한 한 길지만, 푸시할 경우 아무것도 던지지 않고 같은 인스턴스를 재사용할 수 있습니다.

산출량

참고: 출력을 읽기 쉽게 하기 위해 일부 스택 트레이스를 잘랐습니다("[...]).

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은 메모리가 부족하기 전에 예외를 발생시킬 수 있는 충분한 메모리가 있는지 확인합니다.

관리 메모리 환경의 경계를 위반하려는 시도를 나타내는 예외는 해당 환경(이 경우 JVM)의 런타임에 의해 처리됩니다.JVM은 애플리케이션의 IL을 실행하는 자체 프로세스입니다.프로그램이 콜 스택을 제한을 초과하여 확장하거나 JVM이 예약할 수 있는 메모리보다 더 많은 메모리를 할당하려고 하면 런타임 자체에 예외가 주입되어 콜스택이 언해동됩니다.프로그램이 현재 필요로 하는 메모리의 양이나 콜 스택의 깊이에 관계없이 JVM은 해당 예외를 생성하여 코드에 삽입할 수 있는 충분한 메모리를 자체 프로세스 범위 내에 할당합니다.

JVM이 Java 프로그램을 실행하는 JVM에 의해 예약된 가상 메모리와 JVM이 네이티브 프로세스로 실행되는 호스트 OS의 네이티브 메모리를 혼동하고 있는 것 같습니다.시스템의 JVM은 Java 프로그램을 실행하기 위해 JVM이 예약한 메모리가 아니라 OS에서 관리하는 메모리에서 실행됩니다.

추가 정보:

마지막으로 java.lang을 잡으려고 합니다.스택 트레이스를 인쇄하기 위한 오류(및 그 하위 클래스)는 유용한 정보를 제공하지 않을 수 있습니다.대신 힙 덤프를 원합니다.

@Graham Borland의 답변을 더욱 명확하게 하기 위해 JVM은 기동 시에 다음과 같이 합니다.

private static final OutOfMemoryError OOME = new OutOfMemoryError();

나중에 JVM은 'new', 'anewarray' 또는 'multianewarray' 중 하나의 Java 바이트 코드를 실행합니다.이 명령에 의해 JVM은 메모리 부족 상태에서 여러 단계를 수행합니다.

  1. 네이티브 함수를 호출합니다.allocate().allocate()는 특정 클래스 또는 배열의 일부 새 인스턴스에 대해 메모리를 할당하려고 합니다.
  2. 할당 함수: "JVM")를 합니다.doGC()가비지 수집을 시도합니다.
  3. , 「」가 됩니다.allocate()는 인스턴스용 메모리 할당을 다시 시도합니다.
  4. 이 실패했을 allocate "R" (*), "JVM" allocate()를 합니다.throw OOME;OOE를 사용하다OOE를 이 OOE를 참조해 주세요.OOE로 하다

물론 이는 문자 그대로의 단계가 아닙니다. 구현 시 JVM마다 다르겠지만, 이것이 개괄적인 생각입니다.

(*) 실패하기 전에 여기서 상당한 양의 작업이 발생합니다.JVM은 SoftReference 객체를 클리어하고 세대 콜렉터를 사용할 때 테넌트 세대에 직접 할당을 시도합니다.또한 최종화와 같은 다른 작업도 시도합니다.

이 JVM을 할당한다는 입니다.OutOfMemoryErrors말말옳옳옳옳다다
메모리 부족 상황을 유발하여 이를 테스트할 수 있을 뿐만 아니라 JVM의 힙을 확인할 수도 있습니다(Java 8 업데이트 31에서 Oracle의 Hotspot JVM을 사용하여 슬립을 수행하는 작은 프로그램을 사용했습니다).

「」를 사용합니다.jmapOut Of Memory Error(메모리 부족 오류) 9개입니다.

> jmap - histo 12103 | grep Out Of Memory Error71: 9288 java.2011.Out Of Memory Error(메모리 부족 오류)170:132 [Ljava.lang]Out Of Memory Error(메모리 부족 오류);

다음으로 히프 덤프를 생성할 수 있습니다.

> jmap - map : format = b, file = parament.hprof 12315

Eclipse Memory Analyzer를 사용하여 엽니다.OQL 쿼리에서 JVM이 실제로 사전 할당되어 있는 것으로 나타납니다.OutOfMemoryErrors"CHANGE: "CHANGE: " 。

여기에 이미지 설명 입력

이러한 코드를 사전에 할당하는 Java 8 Hotspot JVM의 코드는 다음과 같습니다(일부 생략).

...
// Setup preallocated OutOfMemoryError errors
k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_OutOfMemoryError(), true, CHECK_false);
k_h = instanceKlassHandle(THREAD, k);
Universe::_out_of_memory_error_java_heap = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_metaspace = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_class_metaspace = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_gc_overhead_limit =
  k_h->allocate_instance(CHECK_false);

...

if (!DumpSharedSpaces) {
  // These are the only Java fields that are currently set during shared space dumping.
  // We prefer to not handle this generally, so we always reinitialize these detail messages.
  Handle msg = java_lang_String::create_from_str("Java heap space", CHECK_false);
  java_lang_Throwable::set_message(Universe::_out_of_memory_error_java_heap, msg());

  msg = java_lang_String::create_from_str("Metaspace", CHECK_false);
  java_lang_Throwable::set_message(Universe::_out_of_memory_error_metaspace, msg());
  msg = java_lang_String::create_from_str("Compressed class space", CHECK_false);
  java_lang_Throwable::set_message(Universe::_out_of_memory_error_class_metaspace, msg());

  msg = java_lang_String::create_from_str("Requested array size exceeds VM limit", CHECK_false);
  java_lang_Throwable::set_message(Universe::_out_of_memory_error_array_size, msg());

  msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false);
  java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg());

  msg = java_lang_String::create_from_str("/ by zero", CHECK_false);
  java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg());

  // Setup the array of errors that have preallocated backtrace
  k = Universe::_out_of_memory_error_java_heap->klass();
  assert(k->name() == vmSymbols::java_lang_OutOfMemoryError(), "should be out of memory error");
  k_h = instanceKlassHandle(THREAD, k);

  int len = (StackTraceInThrowable) ? (int)PreallocatedOutOfMemoryErrorCount : 0;
  Universe::_preallocated_out_of_memory_error_array = oopFactory::new_objArray(k_h(), len, CHECK_false);
  for (int i=0; i<len; i++) {
    oop err = k_h->allocate_instance(CHECK_false);
    Handle err_h = Handle(THREAD, err);
    java_lang_Throwable::allocate_backtrace(err_h, CHECK_false);
    Universe::preallocated_out_of_memory_errors()->obj_at_put(i, err_h());
  }
  Universe::_preallocated_out_of_memory_error_avail_count = (jint)len;
}
...

이 코드는 JVM이 먼저 스택트레이스용 공간이 있는 사전 할당 오류 중 하나를 사용하려고 하고 다음으로 스택트레이스가 없는 에러로 폴백하는 것을 나타냅니다.

oop Universe::gen_out_of_memory_error(oop default_err) {
  // generate an out of memory error:
  // - if there is a preallocated error with backtrace available then return it wth
  //   a filled in stack trace.
  // - if there are no preallocated errors with backtrace available then return
  //   an error without backtrace.
  int next;
  if (_preallocated_out_of_memory_error_avail_count > 0) {
    next = (int)Atomic::add(-1, &_preallocated_out_of_memory_error_avail_count);
    assert(next < (int)PreallocatedOutOfMemoryErrorCount, "avail count is corrupt");
  } else {
    next = -1;
  }
  if (next < 0) {
    // all preallocated errors have been used.
    // return default
    return default_err;
  } else {
    // get the error object at the slot and set set it to NULL so that the
    // array isn't keeping it alive anymore.
    oop exc = preallocated_out_of_memory_errors()->obj_at(next);
    assert(exc != NULL, "slot has been used already");
    preallocated_out_of_memory_errors()->obj_at_put(next, NULL);

    // use the message from the default error
    oop msg = java_lang_Throwable::message(default_err);
    assert(msg != NULL, "no message");
    java_lang_Throwable::set_message(exc, msg);

    // populate the stack trace and return it.
    java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(exc);
    return exc;
  }
}

언급URL : https://stackoverflow.com/questions/9261705/what-happens-when-theres-insufficient-memory-to-throw-an-outofmemoryerror

반응형