JVM 发生OutOfMemoryError异常的八种原因:
- java堆空间。
- GC开销超过限制。
- 请求的数组大小超过虚拟机限制。
- Perm gen空间。
- Metaspace。
- 无法新建本机线程。
- 发生了Stack_trace_with_native_method 。
java堆空间:
造成原因:
- 无法在堆中分配对象。
- 吞吐量增加。
- 应用程序无意保存了对象的引用,对象无法被GC回收。
- 应用程序过度使用finalizer 。 finalizer对象不能被GC立刻回收。finalizer由结束队列服务的守护线程调用,有时finalizer线程的处理能力无法跟上结束队列的增长。
解决方案:
- 使用 - Xmx 增加堆大小。
- 尽量避免大的对象的申请,像文件上传,大批量从数据库获取数据,这是需要避免的,尽量分块或者分批处理,有助于系统的正常稳定执行。
- 修复应用程序中的内存泄露。
- 尽量提高一次请求的执行速度,垃圾回收越早越好,否则大量并发来临时,请求就无法分配内存了,容易造成系统雪崩。
GC 开销超过限制:
造成原因:
- 当应用程序耗尽所有可用内存时,GC开销限制超过了错误,而GC多次未能清除它,这时便会引发java.lang.OutOfMemoryError。当JVM花费大量的时间执行GC,而收效甚微,而一旦整个GC的过程超过限制便会触发错误(默认的jvm配置GC的时间超过98%,回收堆内存低于2%)。
解决方案:
- 使用 -Xmx增加堆大小。
- 使用 -XX: UseGCOverheadLimit 取消GC开销限制。
- 修复应用程序中的内存限制。
请求的数组大小超过虚拟机限制:
造成原因:
- 应用程序试图分配一个超过堆大小的数组;
解决方案:
- 使用 ** - Xmx** 增加堆大小。
- 修复应用程序中分配巨大数据的bug。
Perm gen(永久代)空间:
造成原因:
Perm gen空间包含:
- 类的名字,字段,方法。
- 与类相关的对象数组和类型数组。
- JIT 编译的机器码。
解决方案:
- 使用 -XX: MaxPermSize 增加Permgen大小。
- 不重启应用部署应用程序可能会导致此问题,重启JVM解决。
Metaspace(元空间):
造成原因:
jdk1.8开始,Permgen 就变成了Metaspace,在本机内存分配class元数据,如果metaspace耗尽,则抛出异常。
解决方案:
- 通过命令行设置: -XX: MaxMetaspaceSize 增加metaspace大小。
- 取消 -XX : maxmetsspacedize
- 减小java堆大小,为MetaSpace提供更多的可用空间。
- 为服务器分配更多的内存。
- 可能是应用程序bug。
无法新建本机线程
造成原因:
- 内存不足,无法创建新对象。由于线程在本机内存中创建(需要创建新的虚拟机栈,本地方法栈,程序计数器),报告这个错误表明本机内存空间不足。
解决方法:
- 为机器分配更多的内存。
- 减少java堆空间。
- 修复应用程序中的线程泄露。
- 增加操作系统级别的限制。
- 用户进程数增大(- u)1800 。
- 使用 - Xss 减小线程堆栈大小 。
杀死进程或子进程:
造成原因:
- 内核任务: 内存不足时结束,在可用内存极低得情况下会杀死进程。
解决方案:
- 将进程迁移到不同得机器上。
- 给机器增加更多内存。
这个OOM与其它OOM不同,这是由操作系统而非JVM触发的 。
发生stack_trace_with_method:
造成原因:
- 本机方法(native method) 分配失败。
- 打印的堆栈跟踪信息,最顶层的帧是本地方法。
解决方案:
- 使用操作系统本地工具进行诊断。