一、JAVA 堆溢出
准备java VsualVM 观察jvm内存模型情况
1、保证堆区内存溢出的前提:先设置最小内存足够小,最大内存一致,避免扩展内存。
-Xms10m -Xmx10m
2、设置内存快照文件出来用工具做分析
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test
3、写一个内存溢出的方法,该方法中遍历创建新对象,达到内存溢出。
4、打印结果
5、分析结果
此时生成了快照文件
打开快照文件分析
可以看到创建最多的就是我们的oomObject对象
而且没有内存泄漏(内存泄漏就是没有GC无法回收的对象,也就是GC Root跟踪不到的对象)
二、方法栈溢出
1、配置栈深度,顺便打印GC详情
-Xss128k -XX:+PrintGCDetails
2、写递归方法不断增加变量长度
3、结果
4、分析
我们看到这里发生了栈内存溢出,但是没有堆内存溢出,堆内存溢出是以进程为一个单位的,栈超出深度了但远远达不到堆内存,可以利用多线程执行栈溢出可能可以实现,但是这里控制不好可能会导致系统假死,这里不做示范了。
三、运行时常量池溢出
1、设置虚拟机参数
jdk8中常量池属于堆区的一部分,这里设置堆内存大小
-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test
2、遍历创建新常量,并使用String.intern()方法往常量池中不断插入新常量,达到溢出。
3、分析结果
可以看出不断fullGC,最后还是溢出了
对象String和Char撑爆了内存
总结:现在很多主流框架中,如Spring和Mybatis等,利用代理和反射不断创建新对象和增强属性,也会造成内存溢出,这样的场景就不模拟了,方法大概是死循环一个代理类,可以是CGlib,也可以是jdk自带的Proxy类,不断增强,直至内存溢出。
四、本机直接内存溢出
1、分配本地直接内存,如果不设置,默认与Xmx一致
2、创建一个本地方法执行,这里通过反射Unsafe类来分配内存,但是限制了引导类加载器加载,也就是只有rt包才能创建使用,真正分配内存的方法是allocateMemory();
3、查看结果
4、分析结果
值得注意的是,这里虽然内存溢出了,但是没有生成堆的快照文件,这是因为此时的溢出是计算出来的,并不是虚拟机的内存溢出,所以甚至没有察觉,这种情况,往往可以考虑是不是本地方法的溢出,比如NIO,NIO的原理就是使用了本地内存空间换取时间,在本地创建list数组来处理网络请求,所以数组大了,本地内存也变大。