从内存区的角度分析HotSpot 虚拟机下运行时抛出 OutOfMemoryError(OOM) 的原因。
堆
可以用最小值参数 -Xms 和最大值参数 -Xmx 使堆自动扩展或禁用自动扩展。设置 -XX:+HeapDumpOnOutOfMemoryError 参数可用让虚拟机在出现 OOM 异常时 Dump 出内存堆转储快照以便分析。
如果是堆发生了 OOM,首先要分析是内存泄漏还是内存溢出,也就是确认内存中的对象是否必须存在。如果是内存泄漏,用分析工具确定泄漏对象的类型信息和 GC Roots 引用链信息,进而确定发生泄漏的代码位置。如果是内存溢出,一方面考虑物理内存的大小,调整 -Xms 和 -Xmx 两个参数;另一方面,检查代码中对象的生命周期是否合理,或对算法和数据结构进行优化。
栈
HotSpot 合并了虚拟机栈和本地方法栈,可以通过参数 -Xss 调整栈的容量。实际上,栈不容易发生 OOM 异常,当 -Xss 设置过小或有大量局部变量时,栈会抛出 StackOverFlow 异常。多线程场景下,创建大量线程可能会导致 OOM,因为操作系统对单个进程的内存大小有限制,为了在避免 OOM 的情况下创建更多线程,可以减小每个线程的栈大小。
字符串常量池
JDK 1.6 的 String.intern 方法会将首次遇到的 String 对象复制到永久代,并返回这个对象的引用。
JDK 1.7 的 String.intern 方法不再复制,而是记录首次出现的 String 对象的引用。
方法区
方法区并不是没有可能发生 OOM,因为类的卸载回收条件苛刻,而且许多框架对类增强时会创建更大的类,动态语言为了保持动态性会持续创建类,这些因素都会导致方法区 OOM。
直接内存
可以用参数 -XX:MaxDirectMemorySize 指定大小,默认大小和堆的最大值相同。直接内存发生 OOM 堆特征是 Heap Dump 文件很小且没有明显异常。