JVM解读(四):JVM内存溢出异常分析

时间:2021-07-21 20:57:12

JVM全称是java Virtual Machine(java虚拟机),JVM屏蔽了与各个计算机平台相关的软件和硬件差异。
在接下来的日子里,通过写博客的形式学习JVM,让自己更懂得Java!
本系列文章是对《深入分析javaweb技术内幕》和《深入理解java虚拟机》的总结,欢迎大家一起吐槽,一起进步。
《JVM解读》第一篇:JVM体系结构
《JVM解读》第二篇:JVM类加载器ClassLoader
《JVM解读》第三篇:JVM内存区域
《JVM解读》第四篇:JVM内存溢出异常分析
《JVM解读》第五篇:JVM垃圾收集

在实际的开发中我们可能会遇到各种各样的内存溢出(OutOfMemoryError)问题,我在开发的时候就遇到过这样的情况。当时是报的这样异常

Caused by:java.lang.OutOfMemoryError:PermGen Space
at java.lang.ClassLoader……

造成这个原因的是我们的tomcat下方了好几个web程序,而且每个程序都有大量的Spring ,hibernate的jar包,并且这几个程序的jar基本上都是一样的。这就造成了重复加载的情况,直接导致我的永久区溢出。后来把公共的jar提出来放在一个share文件夹下,这样就可以了。而且Spring,Hibernate,在对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的class可以加载如内存。

java堆溢出
java堆用于存储对象,所有不断的创建对象并且在GC Roots到对象之间有可达路径避免垃圾回收清除这些对象,在对象数量达到最大堆的容量限制后就会产生堆溢出

public class HeapOOM {

static class OOMObject{}

public static void main(String[] args) {
List<OOMObject> list=new ArrayList<HeapOOM.OOMObject>();
while(true)
list.add(new OOMObject());

}

JVM解读(四):JVM内存溢出异常分析

虚拟机栈和本地方法栈溢出
栈容量的设置由参数-Xss参数设置。java虚拟机规范中描述的异常

  • 如果线程请求的栈深度大于虚拟机所运行的最大深度,将抛出*Error
  • 如果虚拟机在扩展时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
public class JavaVMStackOF {

private int stackLength=1;
public void stackLeak()
{
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackOF jvof=new JavaVMStackOF();
try {
jvof.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:"+jvof.stackLength);
throw e;
}

}

}

JVM解读(四):JVM内存溢出异常分析
实验表明:在当个线程下,无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是*Erroe异常
方法区和运行时常量池溢出
运行时 常量池是方法区的一部分。我们通过一个String.intern()方法来测试。String.intern()是一个本地方法,如果字符串常量池中已经包含一个等于此String对象,则返回代表池中这个字符串的String对象;否则将此String对象包含的字符串添加到常量池中,并且返回该String对象的引用。

public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
//使用list保持对常量池引用,避免Full GC回收常量池
List<String> list=new ArrayList<String>();
int i=0;
while(true)
list.add(String.valueOf(i++).intern());

}

JVM解读(四):JVM内存溢出异常分析
这里我一开始是用jre7的,结果没反应,改为jre6才有产生这样的情况。
JVM上的动态语言通常会持续创建类来实现语言的动态性,也会经常造成PermGen space异常

直接内存溢出
DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,默认与java堆的最大值-Xmx一样。

public class DirectMemoryOOM {

private static final int _1MB=1024*1024;

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Field unsafeField=Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe=(Unsafe) unsafeField.get(null);
while(true)
unsafe.allocateMemory(_1MB);
}

JVM解读(四):JVM内存溢出异常分析
由DirectMemory导致的内存溢出,一个很明显的特征是在Heap Dump文件中不会看见明显的异常。如果发现OOM情况后Dump文件很小,而程序中又使用了NIO,可以检查是不是直接内存溢出。