JVM全称是java Virtual Machine(java虚拟机),JVM屏蔽了与各个计算机平台相关的软件和硬件差异。
在接下来的日子里,通过写博客的形式学习JVM,让自己更懂得Java!
本系列文章是对《深入分析javaweb技术内幕》和《深入理解java虚拟机》的总结,欢迎大家一起吐槽,一起进步。

在实际的开发中我们可能会遇到各种各样的内存溢出(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());

    }

Java如何监控虚拟机栈内存 如何监控jvm内存溢出_Java如何监控虚拟机栈内存

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

  • 如果线程请求的栈深度大于虚拟机所运行的最大深度,将抛出StackOverflowError
  • 如果虚拟机在扩展时无法申请到足够的内存空间,则抛出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;
        }

    }

}

Java如何监控虚拟机栈内存 如何监控jvm内存溢出_outofmemoryerror_02


实验表明:在当个线程下,无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是StackOverflowErroe异常

方法区和运行时常量池溢出

运行时 常量池是方法区的一部分。我们通过一个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());

    }

Java如何监控虚拟机栈内存 如何监控jvm内存溢出_Java如何监控虚拟机栈内存_03


这里我一开始是用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);
    }

Java如何监控虚拟机栈内存 如何监控jvm内存溢出_内存溢出_04


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