1、Java heap space
描述:当堆内存没有足够空间存放新创建的对象的时候,就会抛java.lang.OutOfMemoryError:Javaheap Space的错误
产生原因
- 原因1:请求创建一个超大对象,比如说一个大数组
- 原因2:超出预期的访问量/数据量,比如上游系统请求流量飙升
- 原因3:过度使用终结器(Finalizer) ,该对象没有立即被gc
- 原因4:内存泄漏(memory leak),大量对象引用没有释放,JVM无法自动对他们回收,比如使用了File等资源没有被回收
解决方案
- 一般只要用-Xmx参数调高JVM的堆内存空间即可
- 如果还没解决
- 如果是超大对象,则检查其合理性,比如是否一次查询了数据库全部结果,而没有对结果数限制
- 如果是业务峰值压力,则可以考虑添加机器资源,或者做限流降级
- 如果是内存泄漏,则要找到持有的对象,修改代码设计,比如关闭没有释放的连接
2、GC overhead limit exceeded
描述
当进程花费了98%以上的时间执行GC,但只恢复了不到2%的内存,且该动作连续重复了5次,就会抛 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误
产生原因
应用程序已经消耗完了所有的可用内存,gc也无法回收. 具体原因类似 Java Heap Space
解决方案
类似 Java Heap Space
Permgen space
描述
永久代Permanent Generation)已经用满,一般是加载的class数目太多或者体积过大。Permgen使用量和加载到内存中的class的数量以及大小正相关
产生原因
- 原因1:加载/缓存到内存中的class定义,包括类的名字,字段,方法和字节码
- 原因2:常量池
- 原因2: 对象数组/类型数组所关联的class
- 原因4:JIT编译器优化后的class信息
解决方案
- 如果是程序启动报错,则通过 -XX:MaxPermSize启动参数,来调大永久代空间。
- 应用重新部署出错,可能是应用没有重启,导致加载了多份Class信息,只需要重启JVM即可。
- 运行时报错,应用程序可能动态创建大量class, 而这些class的生命周期很短暂,但是JVM默认不会卸载class .可以设置 -XX:+CMSClassUnloadingEnabled 和-XX:+UseConcMarkSweepGC这两个参数允许JVM卸载class
- 如果还没解决,可以通过jmap命令dump内存对象 ,然后利用eclipse mat工具分析开销最大的classloader和重复的class
3、Metaspace
描述
JDK1.8中采用了metaspace替换了永久代 (Permanent Generation),该错误表示Metaspace已经被用满
产生原因
同Permgen space
解决方案
同Permgen Space,区别就是调整Metaspace空间大小的启动参数是 -XX:MaxMetaspaceSize
4、Unable to create new native thread
描述
每个Java线程都需要占用一定内存空间,当JVM向底层操作系统请求创建一个新的native线程时,如果没有足够的资源分配,就会报这个错误
产生原因:
- 原因1: 线程数超过了操作系统最大线程数ulimit限制
- 原因2:线程数超过了kernel.pid_max(只能重启)
- 原因3:native内存不足
过程描述
- JVM内部的应用程序请求创建一个新的Java线程
- JVM的native方法代理了这次请求,并向操作系统请求创建一个native线程
- 操作系统尝试创建一个native线程,并为其分配内存
- 如果操作系统虚拟内存耗尽,或者受到32位的进程的地址空间限制,操作系统会拒绝这次native内存分配
- JVM会抛出 java.lang.OutOfMemoryError: Unable to create new native thread的错误
解决方案:
- 升级机器配置,为机器提供更多的内存
- 降低 java heap space的大小
- 修复应用程序的线程泄漏问题
- 限制线程池大小
- 使用-Xss参数减少线程栈的大小
- 调整OS层面的线程最大数: 执行ulimit -a查看,使用 ulimit -u xxx来调整最大线程数限制
5、Out of swap space
描述
所有可用的虚拟内存都被耗尽。虚拟内存(virtual memory)=物理内存(physical memory)+交换空间(swap space)组成
原因分析:
- 原因1:地址空间不足
- 原因2:物理内存被耗光
- 原因3:应用程序的本地内存泄漏(native leak),比如不断申请本地内存,但不释放
- 原因4: 执行 jmap -histo:live命令,强制执行full gc; 如果执行几次内存明显下降,则基本确认是 direct buffer问题
解决方案:
- 升级地址空间为 64 bit
- 使用arthas检查是否为 inflater/deflater解压缩问题,如果是,则显示调用end方法
- 对于 direct bytebuffer问题可以通过启动参数 -XX:MaxDirectMemorySize降低阈值
- 升级服务器配置/隔离部署
6、Requested array size exceeds VM limit
JVM限制了数组的最大长度,这个错误表示程序请求创建的数组超过了最大长度限制
原因分析:
- JVM在为数组分配内存前,会检查要分配的数据结构在系统中是否可以寻址,通常为Integer.MAX_VALUE-2
7、Direct buffer memory
Java允许应用程序通过Direct ByteBuffer直接访问堆外内存,许多高性能程序通过Direct ByteBuffer结合内存映射文件(Memory Mapped File)实现高速I/O
原因分析
- Direct ByteBuffer的默认大小为64MB,如果一旦使用超过显示,就会抛出DirectBuffer memory的错误
解决方案
- Java只能通过ByteBuffer.allocateDirect方法来使用Direct ByteBuffer。可以通过Arthas等在线诊断工具拦截该方法进行排查。
- 检查是否直接或者间接使用了NIO,如netty,jetty等
- 通过启动参数 -XX:MaxDirectMemorySize调整 Direct ByteBuffer的上限
- 检查JVM参数是否有 -XX:+DisableExplicitGC选项,如果有就去掉,因为该参数会使System.gc()失效
- 检查堆外内存使用代码,确认是否会有内存泄漏。或者通过反射调用sun.misc.Cleaner的clean()方法来主动释放被Direct ByteBuffer持有的内存空间。