INDEX
§1 StackOverFlowError
- StackOverFlowError 是栈溢出错误,Error 可以将 JVM 玩崩
- 因为 JVM 的虚拟机栈通常是 1M 的,所以相对容易触发
- 造成 StackOverFlowError 的最常见场景是 超长递归
§2 OutOfMemoryError
- 内存溢出错误,Error 可以将 JVM 玩崩
- 根据溢出部位和原因的不同,OutOfMemoryError 分如下几种常见分支
§2.1 java heap space
- java heap space 是 JVM 中 堆 不够用了
弄一个出来
设置 JVM 只有 3M 的堆 -Xms3m -Xmx3m
可以玩命创建对象
for(String s = "";;) s+= UUID.randomUUID();
也可以一口气来个大的
int[] a = new int[10 * 1024*1024];
§2.2 GC overhead limit exceeded
- GC 开销超出限制
- 常出现 GC 时间占一个 java 进程 98% 的总时间以上,并且至少已经这样进行了 5 次 GC 后
官方说明
The detail message “GC overhead limit exceeded” indicates that the garbage collector is running all the time and Java program is making very slow progress. After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown. This exception is typically thrown because the amount of live data barely fits into the Java heap having little free space for new allocations.
Action: Increase the heap size. The java.lang.OutOfMemoryError exception for GC Overhead limit exceeded can be turned off with the command line flag -XX:-UseGCOverheadLimit.
“GC overhead limit exceeded” 说明 java 进程处理的很慢,因为总是在 GC
一次 GC 之后,如果 java 进程花费了 98% 以上的时间用于 GC 并且它只回收了不到 2% 堆空间,同时这样的 GC 至少已经发生了 5 次以上,就会抛出 java.lang.OutOfMemoryError
此异常由下面原因导致:堆空间勉强存放了存活的数据,只有很小的空间留给新的指派(对象)使用
行动建议:
- 增加堆的大小
- 通过 JVM 参数
-XX:-UseGCOverheadLimit
关闭 GC 开销限制
弄一个出来
设置 JVM 只有 3M 的堆 -Xms3m -Xmx3m -XX:+PrintGCDetails
public class GcOverheadBoomDemo {
public static void main(String[] args) {
List list = new ArrayList();
for(;;){
list.add(UUID.randomUUID());
}
}
}
§2.3 Direct buffer memory
- JVM 的直接缓冲内存耗尽
Direct buffer memory 指的是 直接内存 的一部分,作为 NIO 的 Buffer 区使用,直接内存 的相关内容 点此 - 常出现于通过 NIO 的
ByteBuffer.allocteDirect(capability)
频繁使用直接缓冲内存,且没有保证 DirectByteBuffer 指向的直接内存及时 GC 时allocteDirect(capability)
会在直接内存申请一块 Direct buffer,并通过 jvm 堆 中的 DirectByteBuffer 对象引用和操作- 这么做免于数据从 native 堆 复制到 jvm 堆 的开销,也免于因此造成的 GC 的开销
- DirectByteBuffer 对象不可达时,GC 过程中会回收对应的直接缓冲内存
- 若 DirectByteBuffer 对象进入老生代,则只有 Full GC 才有可能回收对应的直接缓冲内存
- 使用 IOUtil 操作非 DirectBuffer IO 操作时,会使用一个临时的 DirectByteBuffer 来实际执行,并在使用后将它缓存到 ThreadLocal,因此可能造成 GC 不及时
- 可以通过 JVM 参数
-XX:MaxDirectMemorySize
配置较大的直接内存
弄一个出来
设置 JVM 只有 5M 的直接内存 -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
ByteBuffer buffer = ByteBuffer.allocateDirect(30 * 1024*1024);
§2.5 Metaspace
- 元空间耗尽
- 常出现于大量通过 CGlib 创建代理类(关键是类,不是对象)导致大量占用元空间的场景
- 通过声明一个常量,常量引用大对象是不会触发此报错的,详细可见 基础 | JVM - [内存模型 & JMM]
弄一个出来
static class SDf{
}
public static void generate(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SDf.class);
enhancer.setUseCache(false);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invoke(o,args));
enhancer.create();
}
public static void main(String[] args) {
for(int i=0;;i++){
generate(args);
System.out.println(i);
}
}
§2.5 unable to create new native thread
- 不能创建新的本地线程
JVM 在开新线程时是通过private native void start0();
进行的,可能这个本地方法就是提示信息中 native 的由来 - 常出现与应用创建了太多线程,以致于超出系统限制
Linux 默认单个进程可以创建的线程数为 1024 - 通常情况下,需要降低应用线程数
- 一定需要这么多线程时需要变更系统设置
弄一个出来
不要随便尝试,尤其不要用 windows 尝试
不要随便尝试,尤其不要用 windows 尝试
不要随便尝试,尤其不要用 windows 尝试
我就想截个效果图,死机了 5 次,看到是看见了,但是已经无法截图了,每次都是 cpu 100,内存 100
for(;;){
new Thread(()->{
try { TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); }
}).start();
}
修改 Linux 线程数设置
查看当前系统允许创建的线程数
ulimit -u
修改对应配置
vim /etc/security/limits.d/90-nproc.conf
添加
用户名 soft nproc 新数值
默认情况下,root 用户无限制,其他用户 1024