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持有的内存空间。