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)
우리는 알고 있습니다 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 异常.
런타임 상수 풀 오버플로
, 上面所说的 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()); } } }
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(); } } }
/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
//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(); } } }
>>> 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)