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());
}
虚拟机栈和本地方法栈溢出
栈容量的设置由参数-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;
}
}
}
实验表明:在当个线程下,无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是*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());
}
这里我一开始是用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);
}
由DirectMemory导致的内存溢出,一个很明显的特征是在Heap Dump文件中不会看见明显的异常。如果发现OOM情况后Dump文件很小,而程序中又使用了NIO,可以检查是不是直接内存溢出。