博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《深入理解java虚拟机》笔记(3)实战:OutOfMemoryError异常
阅读量:5051 次
发布时间:2019-06-12

本文共 4941 字,大约阅读时间需要 16 分钟。

 

一、Java堆溢出

测试代码:

/** * 

Java堆异常测试

* VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\job *

以上参数的含义是:限制Java堆大小为20MB,不可扩展

*

通过此参数可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照

*

将内存溢出快照存储到指定路径:E:\job

*/public class HeapOOM { static class OOMObject {} public static void main(String[] args) { List
list = new ArrayList
(); while(true) { list.add(new OOMObject()); } }}

 执行结果:

java.lang.OutOfMemoryError: Java heap spaceDumping heap to E:\job ...Exception in thread "main" 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:242)    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)    at java.util.ArrayList.add(ArrayList.java:440)    at com.sandy.jvm.chapter02.HeapOOM.main(HeapOOM.java:19)

针对这类异常,可通过分析工具(如Eclipse Memory Analyzer)对异常快照进行分析,找到具体发生异常代码。

 

二、虚拟机栈和本地方法栈的溢出

由于HotSpot虚拟机不区分虚拟机栈和本地方法栈,因此不需要设置-Xoss参数,栈容量只由-Xss参数设定。

针对栈,虚拟机规范了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

测试代码:

/** * VM Args: -Xss128k */public class JavaStackSOF {        private int stackLength = 1;        public void stackLeak() {        stackLength++;        stackLeak();    }        public static void main(String[] args) {        JavaStackSOF oom = new JavaStackSOF();        try{            oom.stackLeak();        }catch(Throwable e) {            System.out.println("stack length:" + oom.stackLength);            throw e;        }    }}

执行结果:

stack length:2101Exception in thread "main" java.lang.StackOverflowError    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:13)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)    at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)     ...

 单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,虚拟机抛出的都是StackOverflowError。

  • 如果虚拟机扩展栈时,无法申请到足够的空间,将抛出OutOfMemoryError异常。

测试代码:

/** * VM Args: -Xss2M (这时候不妨设置大一点点) */public class JavaStackOOM {    private void dontstop() {        while(true) {                    }    }        public void stackLeakThread() {        while(true) {            new Thread() {                @Override                public void run() {                    dontstop();                }            }.start();        }    }        public static void main(String[] args) {        JavaStackOOM oom = new JavaStackOOM();        oom.stackLeakThread();    }}

 

测试结果会抛出:Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

这种情况下,为每个线程栈分配的内存越大,越容易出现内存溢出。

原因是:操作系统为每个进程分配的内存有限制,比如32位windows限制为2GB,栈内存=2GB-Xmx(最大堆容量)-MaxPermSize(最大方法区容量),每个线程分配的栈内存越大,可创建的线程数就越少,越容易把剩下内存耗尽。

 

三、方法区和常量池溢出

String.intern()是一个Native方法,作用是:如果字符串常量池已包含了一个等于String对象的字符串,则返回这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池,并且返回此String对象的引用。

在jdk1.6及之前版本,常量池分配在永久代中,可通过-XX:PermSize和-XX:MaxPermSize限制方法区大小。

/* * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M(限制常量池容量) * */public class RunTimeConstantPoolOutOfMemoryError {    public static void main(String[] args) {        // 使用list保持常量池引用,避免常量池内的数据被垃圾回收清除        List
list = new ArrayList<>(); long i = 0; while (true) { String string = (i++) + ""; list.add(string.intern()); } }}

 

此段代码在jdk6之前的版本中运行时会产生:Exception in thread "main" java.lang.OutOfMemoryError: PermGen space 其中PermGen space指示内存溢出发生在运行时常量池中。

在jdk7的环境中运行得到的结果却是: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  指示内存溢出发生在堆中而不是方法区中的常量池。

因为在 JDK1.2 ~ JDK6 的实现中,HotSpot 使用永久代实现方法区,而从 JDK7 开始 Oracle HotSpot 开始移除永久代,JDK7中符号表被移动到 Native Heap中,字符串常量和类引用被移动到 Java Heap中。在 JDK8 中,永久代已完全被元空间(Meatspace)所取代。

 

四、本机直接内存溢出

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆最大值一样。

测试代码:

/** * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M */public class DirectMemoryOOM {        private static final int _1MB = 1024*1024;        public static void main(String[] args) throws Exception {        Field unsafeFiled = Unsafe.class.getDeclaredFields()[0];        unsafeFiled.setAccessible(true);        Unsafe unsafe = (Unsafe)unsafeFiled.get(null);        while(true) {            unsafe.allocateMemory(_1MB);        }    }}

 

测试结果:

Exception in thread "main" java.lang.OutOfMemoryError    at sun.misc.Unsafe.allocateMemory(Native Method)    at com.sandy.jvm.chapter02.DirectMemoryOOM.main(DirectMemoryOOM.java:22)

 

由DirectMemory导致的内存溢出,明显特征是在Heap Dump文件中不会看见明显的异常,如果发现Dump文件很小,而程序直接或间接使用了NIO,那可以考虑是否由此原因引起。

转载于:https://www.cnblogs.com/myshare/p/8446988.html

你可能感兴趣的文章
noSQL数据库相关软件介绍(大数据存储时候,必须使用)
查看>>
iOS开发——缩放图片
查看>>
HTTP之URL的快捷方式
查看>>
满世界都是图论
查看>>
配置链路聚合中极小错误——失之毫厘谬以千里
查看>>
代码整洁
查看>>
蓝桥杯-分小组-java
查看>>
Java基础--面向对象编程1(类与对象)
查看>>
Android Toast
查看>>
iOS开发UI篇—Quartz2D使用(绘制基本图形)
查看>>
docker固定IP地址重启不变
查看>>
hadoop的wordcount程序
查看>>
[Swift]LeetCode128. 最长连续序列 | Longest Consecutive Sequence
查看>>
[Swift通天遁地]一、超级工具-(9)在地图视图MKMapView中添加支持交互动作的标注图标...
查看>>
js版base64()
查看>>
poj3006---素数筛法
查看>>
c语言结构体排序示例
查看>>
openresty nginx systemtap netdata
查看>>
[Angular] Make a chatbot with DialogFlow
查看>>
sd卡无法启动及zc706更改主频后可以进入uboot无法启动kernel的坑
查看>>