深入理解JVM虚拟机(二):JDK 内存类的异常分析
JVM数据存储
- 堆存储(Heap):对象存储,实际上就是JAVA的数据存储
- 方法堆栈(Method Stack):存储方法调用的关系。
- 永久代(Perm):在JDK1.6及之前,常量数据存储于此区域
异常示例
堆存储异常
package com.wlzjdm.jvm.learning; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * VM args : -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * 模拟内存溢出情况,抛出:java.lang.OutOfMemoryError: Java heap space * 当配置为:[-Xms20m -Xmx20m]时,执行信息:Heap dump file created [27931458 bytes in 0.604 secs],内存中存放了:810325个对象 * 当配置为:[-Xms10m -Xmx10m]时,执行信息:Heap dump file created [13075518 bytes in 0.261 secs],内存中存放了:360145个对象 * 分析.hprof工具为:MemoryAnalyzer * 结论: * <li> * 通过配置-Xms参数设置堆最小值,通过-Xmx设置堆最大值,当堆使用大于最大值得时候就会抛出OutOfMemoryError。 * </li> * <li> * 堆是用于存储内存对象的,当我们写代码的时候,生成的每一个对象都会放在堆中。 * </li> * @author FDD * */ public class HeapOOM { static class HeapObject implements Serializable{ } public static void main(String[] args) throws Exception { List<HeapObject> list = new ArrayList<HeapObject>(); try { //计算一个对象大小。按照序列化方式计算 // ByteArrayOutputStream bos = new ByteArrayOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(bos); // oos.writeObject(new HeapObject()); // System.out.println(bos.toByteArray().length); // oos.close(); // bos.close(); while(true){ list.add(new HeapObject()); } } catch (Throwable e) { System.out.println("List size is :" + list.size()); e.printStackTrace(); } } }
执行结果:
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid44464.hprof ... Heap dump file created [27864823 bytes in 0.523 secs] List size is :810325 java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2245) at java.util.Arrays.copyOf(Arrays.java:2219) at java.util.ArrayList.grow(ArrayList.java:213) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:187) at java.util.ArrayList.add(ArrayList.java:411) at com.wlzjdm.jvm.learning.HeapOOM.main(HeapOOM.java:42)
方法栈溢出
package com.wlzjdm.jvm.learning; /** * VM Args: -Xss128K * 模拟测试本地方法栈溢出情况,抛出:java.lang.StackOverflowError<br> * <ul> * 配置为:-Xss1K时,stack大小为19015 * </ul> * <ul> * 配置为:-Xss2K时,stack大小为18434 * </ul> * <ul> * 配置为:-Xss128K时,stack大小为987 * </ul> * <ul> * 配置为:-Xss512K时,stack大小为8486 * </ul> * <ul> * 配置为:-Xss1M时, stack大小为19264 * </ul> * <ul> * 配置为:-Xss20M时, stack大小为1270590 * </ul> * <b>结论:</b> * <li> * -Xss用于配置本地方法栈大小 * </li> * <li> * 栈是方法区使用的,很容易理解,一般一个方法都是后进先出的。 * </li> * @author FDD * */ public class JavaVMStackESOF { private int stackLength = 1; public void stackLeak(){ stackLength ++; stackLeak(); } public static void main(String[] args) throws Throwable { JavaVMStackESOF javaVMStackESOF = new JavaVMStackESOF(); try { javaVMStackESOF.stackLeak(); } catch (Throwable e) { System.out.println("Stack Length is :" + javaVMStackESOF.stackLength); throw e; } } }
执行结果:
Stack Length is :11429 Exception in thread "main" java.lang.StackOverflowError at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:38) at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39) at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39) at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39) at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39) ………………
永久代溢出
package com.wlzjdm.jvm.learning; import java.util.ArrayList; import java.util.List; /** * VM Args: -XX:PermSize=1M -XX:MaxPermSize=1M <br> * 说明:此项是配置永久代大小,在JDK1.6及之前,常量是存放于永久代中,所以限制永久代大小就简介限制常量池大小<br> * <code> * <b>JDK1.6 Run Result :</b> * <li> * Configuration -XX:PermSize=10M -XX:MaxPermSize=10M : 116804 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space <br> * </li> * <li> * Configuration -XX:PermSize=20M -XX:MaxPermSize=20M : 280572 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space <br> * </li> * <b>JDK > 1.7 Run Result :</b> * <li> * 可以一直运行下去 * </li> * <b>结论:-XX:PermSize和-XX:MaxPermSzie是配置永久代的大小,在JDK1.6及之前,常量是存放于永久代中,所以可以通过配置永久代大小配置敞亮池,而在JDK1.6之后,逐步剔除永久代的事情。</b> * </code> * @author FDD * */ public class RuntimeConstantPoolOOM { public static void main(String[] args) throws Throwable { List<String> list = new ArrayList<String>(); int i = 0; try { while(true){ list.add(String.valueOf(i++).intern()); } } catch (Throwable e) { System.out.println(list.size()); throw e; } } }
运行结果:
280572 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.wlzjdm.jvm.learning.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:34)
方法区溢出
package com.wlzjdm.jvm.learning; import java.lang.reflect.Method; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * VM Args : -XX:PermSize=10M -XX:MaxPermSize=10M <br> * 此例用来模拟方法区异常, * 结果:OutOfMemoryError: PermGen space * @author FDD * */ public class JavaMethodAreaOOM { public static void main(String[] args) throws Exception { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invoke(obj, args); } }); enhancer.create(); } } static class OOMObject { public OOMObject() { } } }
运行结果:
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:256) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:378) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:286) at com.wlzjdm.jvm.learning.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:29) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:395) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) ... 3 more Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) at java.lang.ClassLoader.defineClass(ClassLoader.java:615) ... 8 more
本机内存溢出
package com.wlzjdm.jvm.learning; import java.lang.reflect.Field; import sun.misc.Unsafe; /** * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M</br> * JVM可以通过指定-XX:MaxDirectMemorySize指定,如果不指定,那么默认与JAVA堆最大值-Xmx一样。<br> * 执行结果:Exception in thread "main" java.lang.OutOfMemoryError * @author FDD * */ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception { Field unsaveField = Unsafe.class.getDeclaredFields()[0]; unsaveField.setAccessible(true); Unsafe unsafe = (Unsafe) unsaveField.get(null); while(true){ unsafe.allocateMemory(_1MB); } } }
运行结果
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.wlzjdm.jvm.learning.DirectMemoryOOM.main(DirectMemoryOOM.java:22)
总结
对于一个内存溢出的问题,我们一定要搞清楚是那块儿内存溢出,只有明白那块内存溢出,才知道系统的数据存储分配,然后通过分析Heap Dump文件得出具体的类,根据错误类型定位到具体的问题点(注意:DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常)。
版权声明:本文为博主原创文章,如需转载请声明文件出处。