JVM(一):Java内存区域与内存溢出异常

2018-12-14  本文已影响0人  Jorvi

1. JVM运行时数据区域

2. 直接内存

直接内存不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。NIO中引入了一种基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后用Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这块内存是直接分配在物理内存中的(Java堆外),不受Java堆大小的限制,但是受物理内存大小限制(物理限制和处理器寻址空间限制)。

3. 对象创建

HotSpot虚拟机中,对象的创建过程大致如下:

4. 对象内存布局

HotSpot虚拟机中,对象的内存布局可分为三个区域:对象头、实例数据和对齐填充。

5. 对象访问

Java程序通过上的reference数据来操作上的具体对象。

6. OutOfMemoryError异常

/**
 * JVM启动参数:
 * -verbose:gc 打印GC信息
 * -Xms20m 最小堆内存
 * -Xmx20m 最大堆内存,最大堆内存=最小堆内存时不会自动扩展堆
 * -XX:+HeapDumpOnOutOfMemoryError 当内存溢出时Dump出当前内存中堆的转储快照
 * -XX:HeapDumpPath=D:\dump 堆转储快照保存地址
 */
public class HeapOOM {
    static class OOMObject {

    }

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

        while (true) {
            list.add(new OOMObject());
        }
    }
}

// 异常信息
java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:\dump\java_pid105876.hprof ...
Heap dump file created [26566702 bytes in 0.273 secs]

解决方法:

  1. 利用内存映像分析工具(例如Eclipse Memory Analyzer)分析Dump出来的堆转储快照,确认内存中的对象是否是必要的(即确定是内存泄露还是内存溢出)
  2. 如果是内存泄露,进一步通过工具查看泄露对象到GC Roots的引用链,从而定位泄露代码的位置
  3. 如果不存在内存泄露,则检查JVM的堆参数设置,检查代码中是否存在某些对象生命周期过长、持有状态时间过长等情况。

1)如果某个线程请求的栈深度大于虚拟机所允许的最大深度,则抛出StackOverflowError

/**
 * 虚拟机栈StackOverflow
 * -Xss128k 栈容量大小
 */
public class JVMStackSOF {
    public void stackLeak() {
        stackLeak();
    }

    public static void main(String[] args) {
        JVMStackSOF sof = new JVMStackSOF();

        sof.stackLeak();
    }
}

// 异常信息
Exception in thread "main" java.lang.StackOverflowError
    at oom.JVMStackSOF.stackLeak(JVMStackSOF.java:9)
    at oom.JVMStackSOF.stackLeak(JVMStackSOF.java:9)
    at oom.JVMStackSOF.stackLeak(JVMStackSOF.java:9)

出现StackOverflowError的原因:每个方法执行时都会往栈中压入一栈帧(各栈帧的大小也不一样),当不停的压入栈帧达到虚拟机允许的最大深度时就会抛出StackOverflowError。大多情况下,栈深达到1000-2000没问题,足够正常的方法调用。

2)如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError:


/**
 * 虚拟机栈OutOfMemoryError
 * -Xss2m 每个线程的栈内存
 */
public class JVMStackOOM {
    private void keepRunning() {
        while (true) {

        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    keepRunning();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        JVMStackOOM oom = new JVMStackOOM();
        oom.stackLeakByThread();
    }
}

// 异常信息
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

产生OutOfMemoryError的原因:操作系统给每个进程分配的内存是有限的(例如32位Windows限制为2G),那么:
所有线程的虚拟机栈+本地方法栈+程序计数器内存(可忽略)=进程内存限制-最大堆内存(Xmx)-最大方法区容量(MaxPermSize)。
当所有线程的虚拟机栈+本地方法栈内存超出限制后就会出现OutOfMemoryError。


上一篇 下一篇

猜你喜欢

热点阅读