1.堆内存溢出(Java heap space)
异常信息:java.lang.OutOfMemoryError: Java heap space
可能原因:死循环、递归层数太多、内存泄露。或者系统并发高请求量大内存太小。
解决方案:
- 排查代码 jstack jmap jstat
- 参数设置:-Xms1024m JVM启动内存初始大小 -Xmx3062m JVM启动内存的最大值
2.栈溢出(Thread Stack space)
异常信息:java.lang.StackOverflowError: Thread Stack space
可能原因:单线程调用方法次数太多,一般递归层数太多
解决方案:
- 排查代码
- 参数设置:-Xss512k 设置虚拟机栈大小
3.元空间不足(MetaSpace)
**异常信息:**java.lang.OutOfMemoryError: MetaSpace
**发生原因:**元空间不足
**解决方案:**增大元空间大小,配置参数-XX:MaxMetaspaceSize=xx
示例:
可在启动时配置的小一点,可模拟次异常, 比如设置 -XX:MaxMetaspaceSize=2m
注:
- 元空间初始大小 默认21M
- -XX:MaxMetaspaceSize=512m , 元空间最大可分配大小(一台机器多个服务可配置上限防止影响其他服务)
- 也可能是代码问题,cglib动态代理生成的类过多塞满Metaspace导致溢出。
4.GC效率太低(GC overhead limit exceeded)
异常信息:java.lang.OutOfMemoryError: GC overhead limit exceeded
发生原因:
- 垃圾收集器GC效率很低,jvm花费超过 98%的 CPU 时间来进行一次 GC,
- 但是回收的内存却少于 2%的堆空间大小,并且GC连续超过5次都这样
示例代码
public class GcOverrhead {
public static void main(String[] args){
Map map = System.getProperties();
Random r = new Random();
while (true) {
map.put(r.nextInt(), "value");
}
}
}
启动时加参数:-Xmx45m -XX:+UseParallelGC -XX:+PrintGCDetails运行一段时间,就会出现以下异常。
可通过增加参数-XX:-UseGCOverheadLimit可以避免这个异常,但其实是治标不治本,还需实际定位修改代码问题。
5.数组大小超过限制(VM limit)
**异常信息:**java.lang.OutOfMemoryError: Requested array size exceeds VM limit
发生原因:
- 请求分配的数组太大,导致jvm空间不足
- 请求的数组大于等于Integer.MAX_INT - 1
示例代码1 :直接抛出Requested array size exceeds VM limit
int[] arr = new int[Integer.MAX_VALUE - 1];
示例代码2 :先抛出 Java heap space后再抛出Requested array size exceeds VM limit
for (int i = 3; i >= 0; i--) {
try {
int[] arr = new int[Integer.MAX_VALUE-i];
System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
} catch (Throwable t) {
t.printStackTrace();
}
}
6.创建线程失败(Unable to create threads)
异常信息:java.lang.OutOfMemoryError: Unable to create native threads
可能原因:
-
内存空间不够,jvm启动时参数-Xss指定每个线程占用的堆栈大小,如果内存不够,就会创建线程失败
-
操作系统上ulimit中max user processes参数限制,这个参数指操作系统可以创建的全局线程数量
- 可使用 ulimit -a | grep 'max user processes’命令可以查看,如下图
- 可使用 ulimit -a | grep 'max user processes’命令可以查看,如下图
-
参数sys.kernel.threads-max限制,
-
可通过命令cat /proc/sys/kernel/threads-max来查看,如下图
-
修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.threads-max = 10000
-
-
参数sys.kernel.pid_max限制,这个参数只是每创建一个线程,都需要分配一个pid,当pid的值大于这个值时,就会创建失败。
-
查看命令:cat /proc/sys/kernel/pid_max
-
修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.pid_max =10000
-
7.堆外内存溢出(Direct buffer memory)
异常信息:nio handle failed java.lang.OutOfMemoryError: Direct buffer memory
可能原因:堆外内存不足
解决方案:
- JVM参数设置最大堆外内存:-XX:MaxDirectMemorySize
- 申请堆外内存代码:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);//申请堆外内存1k
1、如果堆外内存足够,就直接预留一部分内存
2、如果堆外内存不足,则将已经被 JVM 垃圾回收的 DirectBuffer 对象的堆外内存释放
3、如果进行一次堆外内存资源回收后,还不够进行本次堆外内存分配的话,则进行 System.gc()
4、如果 9 次尝试后依旧没有足够的可用堆外内存,则抛异常
原因可能是年轻代设置太小,并且关闭了系统gc调用(-XX:DisableExplicitGC)