[java] java.lang.OutOfMemoryError 잡기?

대한 문서java.lang.Error :

오류는 합리적인 응용 프로그램이 포착하려고 시도해서는 안되는 심각한 문제를 나타내는 Throwable의 하위 클래스입니다.

그러나 java.lang.Error의 하위 클래스와 마찬가지로 java.lang.Throwable이러한 유형의 Throwable을 잡을 수 있습니다.

이런 종류의 예외를 잡는 것이 좋은 생각이 아닌 이유를 이해합니다. 내가 아는 한, 우리가 그것을 잡기로 결정하면 catch 핸들러는 자체적으로 메모리를 할당해서는 안됩니다. 그렇지 않으면 OutOfMemoryError다시 던져 질 것입니다.

그래서 제 질문은 :

  1. 잡는 java.lang.OutOfMemoryError것이 좋은 생각 일 수있는 실제 시나리오가 있습니까?
  2. catch하기로 결정한 경우 java.lang.OutOfMemoryErrorcatch 핸들러가 자체적으로 메모리를 할당하지 않는지 어떻게 확인할 수 있습니까 (도구 또는 모범 사례)?


답변

나는 여기에있는 대부분의 답변에 동의하고 동의하지 않습니다.

OutOfMemoryError내 경험 (Windows 및 Solaris JVM에서) 을 잡을 수있는 여러 시나리오가 있지만 JVM에 OutOfMemoryError대한 죽음 은 매우 드물게 발생 합니다.

를 잡을 수있는 좋은 이유는 단 한 가지이며 OutOfMemoryError, 이는 정상적으로 종료하고 리소스를 깨끗하게 해제하고 가능한 한 최선의 실패 이유를 기록하는 것입니다 (아직 가능한 경우).

일반적으로 OutOfMemoryError힙의 나머지 자원으로 만족할 수없는 블록 메모리 할당으로 인해 발생합니다.

이 때 Error힙이 실패 할당 이전과 할당 된 객체의 동일한 금액을 포함 지금 런타임 정리 필요할 수 있습니다 더 많은 메모리를 해제하는 객체에 대한 참조를 드롭 할 때입니다 발생합니다. 이 경우 계속할 수도 있지만 JVM이 복구 가능한 상태에 있다는 것을 100 % 확신 할 수는 없으므로 확실히 나쁜 생각입니다.

OutOfMemoryErrorJVM이 catch 블록의 메모리 부족을 의미하지 않는 데모 :

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" + maxMemory + "M");
        }
    }
}

이 코드의 출력 :

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

중요한 것을 실행하는 경우 일반적 Error으로을 잡아서 syserr에 기록한 다음 선택한 로깅 프레임 워크를 사용하여 기록한 다음 리소스를 해제하고 깨끗한 방식으로 종료합니다. 일어날 수있는 최악의 상황은 무엇입니까? 어쨌든 JVM은 죽어 가고 있고 (또는 이미 죽었다), 잡으면 Error적어도 정리할 기회가있다.

주의 할 점은 정리가 가능한 곳에서만 이러한 유형의 오류를 포착해야한다는 것입니다. 담요하지 마십시오 catch(Throwable t) {}처럼 사방 또는 넌센스.


답변

다음과 같이 복구 할 수 있습니다 .

package com.stackoverflow.q2679330;

public class Test {

    public static void main(String... args) {
        int size = Integer.MAX_VALUE;
        int factor = 10;

        while (true) {
            try {
                System.out.println("Trying to allocate " + size + " bytes");
                byte[] bytes = new byte[size];
                System.out.println("Succeed!");
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("OOME .. Trying again with 10x less");
                size /= factor;
            }
        }
    }

}

하지만 말이 되나요? 그 밖의 무엇을 하시겠습니까? 처음에 그렇게 많은 메모리를 할당하는 이유는 무엇입니까? 적은 메모리도 괜찮습니까? 어쨌든 이미 그것을 사용하지 않는 이유는 무엇입니까? 또는 그것이 가능하지 않다면 JVM에 처음부터 더 많은 메모리를 제공하지 않는 이유는 무엇입니까?

질문으로 돌아 가기 :

1 : java.lang.OutOfMemoryError를 잡을 때 실제 단어 시나리오가 있습니까?

아무 것도 떠오르지 않습니다.

2 : 만약 우리가 java.lang.OutOfMemoryError를 잡았다면 어떻게 catch 핸들러가 자체적으로 메모리를 할당하지 않는다는 것을 확신 할 수 있습니까 (어떤 도구 나 최선의 방법)?

OOME의 원인에 따라 다릅니다. try블록 외부에서 선언되고 단계별로 발생하면 기회가 거의 없습니다. 미리 일부 메모리 공간을 예약 있습니다 .

private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.

그런 다음 OOME 중에 0으로 설정하십시오.

} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}

물론 이것은 전혀 의미가 없습니다.) 응용 프로그램에 필요한만큼 JVM에 충분한 메모리를 제공하십시오. 필요한 경우 프로파일 러를 실행하십시오.


답변

일반적으로 OOM을 잡아서 복구하는 것은 좋지 않습니다.

  1. OOME은 응용 프로그램이 알지도 못하는 스레드를 포함하여 다른 스레드에서도 발생할 수 있습니다. 이러한 스레드는 이제 죽고 알림을 기다리고 있던 모든 스레드가 영원히 멈출 수 있습니다. 간단히 말해, 앱이 종료 될 수 있습니다.

  2. 성공적으로 복구하더라도 JVM은 여전히 ​​힙 부족에 시달릴 수 있으며 결과적으로 애플리케이션은 비정상적으로 수행됩니다.

OOME을 사용하는 가장 좋은 방법은 JVM이 죽도록하는 것입니다.

(이는 JVM 죽는다고 가정합니다 . 예를 들어 Tomcat 서블릿 스레드의 OOM은 JVM을 죽이지 않으며, 이로 인해 Tomcat이 요청에 응답하지 않는 긴장 상태가됩니다. 재시작.)

편집하다

나는 OOM을 전혀 잡는 것이 나쁜 생각이라고 말하는 것이 아닙니다. 문제는 의도적으로 또는 감독을 통해 OOME에서 복구를 시도 할 때 발생합니다. OOM (직접 또는 Error 또는 Throwable의 하위 유형으로)을 발견 할 때마다 다시 발생 시키거나 애플리케이션 / JVM이 종료되도록 조정해야합니다.

참고 : 이것은 OOM에 대한 최대의 견고성을 위해 응용 프로그램이 Thread.setDefaultUncaughtExceptionHandler () 를 사용 하여 OOME이 발생하는 스레드에 관계없이 OOME 이벤트에서 응용 프로그램이 종료되도록하는 핸들러를 설정 해야 함을 시사합니다 . 나는 이것에 대한 의견에 관심이 있습니다 …

유일한 다른 시나리오는 OOM이 부수적 손상을 초래하지 않았 음 을 확신 하는 경우입니다. 즉 :

  • 구체적으로 OOME의 원인,
  • 그 당시 애플리케이션이 무엇을하고 있었는지, 그 계산을 그냥 버리는 것이 괜찮습니다.
  • (대략) 동시 OOME이 다른 스레드에서 발생할 수 없습니다.

이러한 사항을 알 수있는 응용 프로그램이 있지만 대부분의 응용 프로그램에서는 OOME 이후의 계속이 안전한지 확인할 수 없습니다. 당신이 그것을 시도 할 때 그것이 경험적으로 “작동”하더라도.

(문제는 “예상 된”OOME의 결과가 안전하고 “예상되지 않은”OOME이 try / catch OOME의 제어 내에서 발생할 수 없음을 증명하기 위해 공식적인 증거가 필요하다는 것입니다.)


답변

예, 실제 시나리오가 있습니다. 여기 내 것이 있습니다. 노드 당 메모리가 제한된 클러스터에서 매우 많은 항목의 데이터 세트를 처리해야합니다. 주어진 JVM 인스턴스는 여러 항목을 차례로 거치지 만 일부 항목은 클러스터에서 처리 OutOfMemoryError하기에는 너무 큽니다. 어떤 항목이 너무 큰지 파악하고 기록 할 수 있습니다 . 나중에 RAM이 더 많은 컴퓨터에서 큰 항목 만 다시 실행할 수 있습니다.

(실패한 어레이의 단일 멀티 기가 바이트 할당이기 때문에 JVM은 오류를 포착 한 후에도 여전히 문제가 없으며 다른 항목을 처리 할 충분한 메모리가 있습니다.)


답변

OOME을 잡는 것이 합당한 시나리오가 있습니다. IDEA는이를 포착하고 시작 메모리 설정을 변경할 수있는 대화 상자를 표시합니다 (완료하면 종료 됨). 애플리케이션 서버가이를 포착하고보고 할 수 있습니다. 이를 수행하는 핵심은 디스패치의 높은 수준에서 수행하여 예외를 포착하는 지점에서 많은 리소스를 확보 할 수있는 합리적인 기회를 확보하는 것입니다.

위의 IDEA 시나리오 외에도 일반적으로 캐칭은 OOM뿐만 아니라 Throwable이어야하며 적어도 스레드가 곧 종료되는 컨텍스트에서 수행되어야합니다.

물론 대부분의 경우 기억이 굶주리고 상황은 회복 할 수 없지만 말이되는 방법이 있습니다.


답변

내 경우에는 OutOfMemoryError를 잡는 것이 좋은 생각인지 궁금했기 때문에이 질문을 보았습니다. 나는이 오류를 잡는 또 다른 예를 보여주기 위해 부분적으로 대답하고 있습니다.이 오류가 누군가 (예 : 나)에게 이해가 될 수 있고 부분적으로 제 경우에 좋은 아이디어인지 여부를 확인하기 위해 부분적으로 (저는 uber 주니어 개발자이므로 절대 할 수 없습니다 내가 작성한 코드 한 줄에 대해 너무 확신하십시오).

어쨌든 메모리 크기가 다른 여러 장치에서 실행할 수있는 Android 애플리케이션을 개발 중입니다. 위험한 부분은 파일에서 비트 맵을 디코딩하여 ImageView 인스턴스에 표시하는 것입니다. 디코딩 된 비트 맵의 ​​크기 측면에서 더 강력한 장치를 제한하고 싶지 않으며 매우 낮은 메모리로 본 적이없는 일부 고대 장치에서 앱이 실행되지 않을 수도 있습니다. 따라서 나는 이것을한다 :

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = 1;
boolean imageSet = false;
while (!imageSet) {
  try {
    image = BitmapFactory.decodeFile(filePath, bitmapOptions);
    imageView.setImageBitmap(image);
    imageSet = true;
  }
  catch (OutOfMemoryError e) {
    bitmapOptions.inSampleSize *= 2;
  }
}

이런 식으로 사용자 또는 사용자의 요구와 기대에 따라 점점 더 강력한 장치를 제공 할 수 있습니다.


답변

예, 진짜 질문은 “예외 처리기에서 무엇을 하시겠습니까?”입니다. 거의 모든 유용한 경우 더 많은 메모리를 할당합니다. OutOfMemoryError가 발생할 때 진단 작업을 수행 -XX:OnOutOfMemoryError=<cmd>하려면 HotSpot VM에서 제공 하는 후크를 사용할 수 있습니다 . OutOfMemoryError가 발생하면 명령을 실행하고 Java 힙 외부에서 유용한 작업을 수행 할 수 있습니다. 애초에 애플리케이션의 메모리 부족을 방지하기를 원하므로 그 이유를 파악하는 것이 첫 번째 단계입니다. 그런 다음 MaxPermSize의 힙 크기를 적절하게 늘릴 수 있습니다. 다른 유용한 HotSpot 후크는 다음과 같습니다.

-XX:+PrintCommandLineFlags
-XX:+PrintConcurrentLocks
-XX:+PrintClassHistogram

여기 에서 전체 목록보기