>  기사  >  Java  >  Java 공통 메모리 오버플로 예외 및 코드 구현

Java 공통 메모리 오버플로 예외 및 코드 구현

黄舟
黄舟원래의
2017-02-23 10:38:041416검색

Java Heap OutOfMemoryError

Java 힙은 객체 인스턴스를 저장하는 데 사용되므로 계속 객체를 생성하고 GC 루트와 생성된 객체 사이에 도달 가능한 경로가 있는지 확인하여 객체 인스턴스가 가비지 컬렉팅 중이고 너무 많은 객체가 생성되면 힙 메모리가 부족해 OutOfMemoryError 예외가 발생하게 됩니다.

/**
 * @author xiongyongshun
 * VM Args: java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
 */
public class OutOfMemoryErrorTest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        int i = 0;
        while (true) {
            list.add(i++);
        }
    }
}

위는 OutOfMemoryError 예외를 발생시키는 코드입니다. . 가비지 수집을 방지하기 위해 지속적으로 개체를 독립적으로 생성하고 목록에 저장하는 것을 볼 수 있습니다. 따라서 개체가 너무 많으면 힙 메모리가 오버플로됩니다.

java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError를 사용하여 힙 메모리를 10MB로 설정하고 -XX:+HeapDumpOnOutOfMemoryError 매개 변수를 사용하여 OutOfMemoryError 예외가 발생할 때 JVM이 현재 메모리 스냅샷을 인쇄하도록 합니다. 이후의 편의를 위해 분석합니다.

위 코드를 컴파일하고 실행하면 다음과 같은 결과가 출력됩니다.

>>> java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError com.test.OutOfMemoryErrorTest                                                                                            16-10-02 23:35
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1810.hprof ...
Heap dump file created [14212861 bytes in 0.125 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3210)
        at java.util.Arrays.copyOf(Arrays.java:3181)
        at java.util.ArrayList.grow(ArrayList.java:261)
        at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
        at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
        at java.util.ArrayList.add(ArrayList.java:458)
        at com.test.OutOfMemoryErrorTest.main(OutOfMemoryErrorTest.java:15)

Java stack StackOverflowError

우리는 알고 있습니다 JVM의 런타임 데이터 영역에는 가상 머신 스택이라는 메모리 영역이 있습니다. 이 영역의 역할은 각 메소드가 실행될 때 로컬 변수 테이블, 피연산자 스택을 저장하는 데 사용되는 스택 프레임을 생성하는 것입니다. 메소드 종료 및 기타 정보.

따라서 무한 재귀 호출을 생성할 수 있습니다. 재귀 깊이가 너무 크면 스택 공간이 소진되어 StackOverflowError 예외가 발생합니다. >구체적인 코드는 다음과 같습니다.


/**
 * @author xiongyongshun
 * VM Args: java -Xss64k
 */
public class OutOfMemoryErrorTest {
    public static void main(String[] args) {
        stackOutOfMemoryError(1);
    }
    public static void stackOutOfMemoryError(int depth) {
        depth++;
        stackOutOfMemoryError(depth);
    }
}

위 코드를 컴파일하고 실행하면 다음과 같은 예외 메시지가 출력됩니다.


Exception in thread "main" java.lang.StackOverflowError
    at com.test.OutOfMemoryErrorTest.stackOutOfMemoryError(OutOfMemoryErrorTest.java:27)

메소드 영역 메모리 오버플로

, 因为 JDK8 已经移除了永久代, 取而代之的是 metaspace, 因此在 JDK8 中, 下面两个例子都不会导致 java.lang.OutOfMemoryError: PermGen space 异常.


런타임 상수 풀 오버플로

Java 1.6 및 이전 HotSpot JVM 버전에는 영구 생성 개념이 있습니다. 즉, GC의 세대별 수집 메커니즘을 메소드 영역으로 확장한 것입니다. 메소드 영역에서는 메모리의 일부를 상수 풀을 저장하는 데 사용하므로 코드에 상수가 너무 많으면 상수 풀 메모리가 사용됩니다. 소모되어 메모리 오버플로가 발생합니다. 그러면 상수 풀에 많은 수의 상수를 추가하는 방법은 String.intern() 메서드에 의존해야 한다는 것입니다. 이 String의 값이 이미 상수 풀에 있으면 이 메서드는 상수 풀에 있는 해당 문자열의 참조를 반환합니다. 그렇지 않으면 이 String에 포함된 값이 상수 풀에 추가되고 이 String 객체에 대한 참조를 반환합니다. JDK 1.6 이하 버전에서는 영구 생성에서 상수 풀이 할당되므로 "-XX:PermSize" 및 "-XX:MaxPermSize" 매개 변수를 설정하여 상수 풀의 크기를 간접적으로 제한할 수 있습니다.


, 上面所说的 String.intern() 方法和常量池的内存分布仅仅针对于 JDK 1.6 及之前的版本, 在 JDK 1.7 或以上的版本中, 由于去除了永久代的概念, 因此内存布局稍有不同.

다음은 상수 풀 메모리 오버플로를 구현하는 코드 예제입니다.



/**
 * @author xiongyongshun
 * VM Args:  -XX:PermSize=10M -XX:MaxPermSize=10M 
 */
public class RuntimeConstantPoolOOMTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

이 예제에서는 String.intern( ) 메소드를 사용하여 상수 풀에 많은 수의 문자열 상수를 추가함으로써 상수 풀의 메모리 오버플로가 발생합니다.

JDK1.6을 통해 컴파일하고 컴파일합니다. 위의 코드를 실행하면 다음과 같은 문제가 발생합니다. 다음 출력:


Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
        at java.lang.String.intern(Native Method)
        at com.test.RuntimeConstantPoolOOMTest.main(RuntimeConstantPoolOOMTest.java:16)

, 如果通过 JDK1.8 来编译运行上面代码的话, 会有如下警告, 并且不会产生任何的异常:
>>> java -XX:PermSize=10M -XX:MaxPermSize=10M com.test.RuntimeConstantPoolOOMTest                                                                                                  16-10-03 0:23
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0

메서드 영역의 메모리 오버플로

메서드 영역은 클래스 이름, 클래스 액세스 수정자, 필드 등 클래스 관련 정보를 저장하는 데 사용됩니다. 따라서 메소드 영역이 너무 작고 너무 많은 클래스를 로드하면 메소드 영역의 메모리가 오버플로됩니다.


//VM Args:  -XX:PermSize=10M -XX:MaxPermSize=10M
public class MethodAreaOOMTest {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MethodAreaOOMTest.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(o, objects);
                }
            });
            enhancer.create();
        }
    }
}

위 코드에서, 우리는 CGlib를 사용하여 많은 수의 클래스를 동적으로 생성합니다. JDK6에서 위 코드를 실행하면 OutOfMemoryError: PermGen 공간 예외가 생성됩니다:

/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home /bin/java -jar -XX:PermSize=10M -XX:MaxPermSize=10M target/Test-1.0-SNAPSHOT.jar
출력 결과는 다음과 같습니다.


Caused by: java.lang.OutOfMemoryError: PermGen space
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
        ... 11 more

MetaSpace 메모리 오버플로

메서드 영역의 메모리 오버플로 섹션에서 JDK8에는 영구 생성 개념이 없으므로 이 두 예제는 JDK8에서 예상한 결과를 얻지 못했다고 언급했습니다. 메소드 영역 메모리 오버플로 같은 오류도 있죠? 물론 JDK8에서는 MetaSpace 영역을 Class 관련 정보를 저장하는 용도로 사용하기 때문에 MetaSpace 메모리 공간이 부족하면 java.lang.OutOfMemoryError: Metaspace Exception이 발생합니다. 🎜>

위에서 언급한 예를 계속 사용합니다.



//VM Args: -XX:MaxMetaspaceSize=10M
public class MethodAreaOOMTest {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MethodAreaOOMTest.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(o, objects);
                }
            });
            enhancer.create();
        }
    }
}

이 예의 코드 부분은 변경되지 않았습니다. 유일한 차이점은 JDK8을 사용하여 이를 실행해야 한다는 것입니다. 코드는 -XX:MaxMetaspaceSize=10M 매개변수로 설정됩니다. 이 매개변수는 Metaspace의 최대 크기가 10M임을 JVM에 알려줍니다.

그런 다음 JDK8을 사용하여 이 예제를 컴파일하고 실행하면 다음 예외가 출력됩니다. :

>>> java -jar -XX:MaxMetaspaceSize=10M target/Test-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
    at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
    at com.test.MethodAreaOOMTest.main(MethodAreaOOMTest.java:22)

위 내용은 일반적인 Java 메모리 오버플로 예외 및 코드 구현 내용입니다. 자세한 내용은 PHP 중국어 웹사이트(www.php.cn)를 참고하세요!





-->
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.