本文已参与「新人创作礼」活动,一起开启掘金创作之路。
OOM
内存泄漏
概念
内存泄漏:对象不会再被程序用到,并且也不能被GC回收时,产生内存泄漏。
广义的“内存泄漏”:不好的实践导致对象的生命周期过长,甚至导致OOM;
尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现OutOfMemory异常,导致程序崩溃。
例子:
(1)单例对象的生命周期和应用程序一样长,如果单例对象持有外部对象的引用,会使外部对象不能被回收,导致内存泄漏。
(2)使用网络连接、数据库连接、IO连接等没有close,则不能被回收
(3)不能逃逸的对象在类中创建,不必要的静态引用类型,会导致对象生命周期过长而产生内存泄漏。
分析方法:工具:jprofiler、visualvm、eclipse的MAT等
(1)分析dump文件,确认内存中的对象是否是必要的,分析出发生了内存泄漏还是内存溢出。
(2)检查代码中是否存在对象生命周期过长的情况,减少不必要的静态引用类型,如只在方法中使用的对象在方法中声明。
解决:内存泄漏,可以通过工具查看泄漏对象的类型信息以及它的GC Roots引用链,可以比较准确的定位泄漏代码的位置。
内存溢出
概念:没有空闲内存(可能是堆大小设置不合理),并且垃圾收集器也无法提供更多内存。
分析:如果内存中的对象都必须活着,那就是发生了内存溢出问题。
解决:(1)分析dump文件,观察方法区和堆哪个内存溢出,尝试将相应区的物理内存调大(-Xms,-Xmx)
(2)检查代码中是否存在对象生命周期过长的情况,减少不必要的静态引用类型,如只在方法中使用的对象在方法中声明。
(3)是否有大量使用反射、动态代理等的情况,造成方法区的溢出。
比如,方法区虽然是在本地内存中,但是在大量使用反射、动态代理的场景中,也可能出现OOM
intern方法不断向字符串常量池中添加字符串常量也可能导致方法区的OOM(实际报错为堆溢出)。
直接内存溢出
直接内存并不是虚拟机运行时数据区域的一部分,并且不受堆内存的限制,但是受到机器内存大小的限制。常见的比如在NIO中可以使用native函数直接分配堆外内存就容易导致OOM的问题。
直接内存大小可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java 堆最大值-Xmx一样。
由直接内存导致的内存溢出,一个明显的特征是在Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑检查一下是不是这方面的原因。
参数设置

内存空间、堆空间设置参数
-Xms 初始堆大小
-Xmx 堆最大大小
-XX:NewSize 新生代大小
-XX:MaxNewSize 新生代最大的大小
-XX:MaxRatio 老年代与新生代比例
打开逃逸分析
【-XX:+DoEscapeAnalysis】 将+改为-则是不开启逃逸分析。
生成dump文件
方式一:jvm参数,发生OOM时会生成dump文件。
-XX:+HeapDumpOnOutOfMemoryError
方式二:jmap命令用dump参数可以生成dump文件。file后面是保存的文件名称,1246则是java程序的PID。
jmap -dump:format=b,file=user.dump 1246