想办法让 Jvm 的各个分区 OOM
堆内存#
堆内存溢出主要是一直创建对象,但是又不能被 GC 回收(可达性分析,GC Root 和对象之间有可达路径)就会溢出了。
参数:-Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError
byte[] a = new byte[21 * 1024 * 1024];
加上HeapDumpOnOutOfMemoryError
可以 dump 出日志,便于后续分析 OOM 的问题所在
栈内存溢出#
虚拟机栈里有个局部变量表,通过不断创建局部变量,可以让它爆掉
(这里没成功 OOM, 不懂是 Jvm 的原因还是什么)
参数: -Xss1m -XX:+HeapDumpOnOutOfMemoryError
public void StackOOM() {
while (true) {
Thread thread = new Thread(this::job);
thread.start();
}
}
private void job() {
while (true) {
}
}
方法区溢出#
方法区用于存放类的信息,所以想要溢出方法区,只要动态地生成大量的类就可以了。比较简单地生成类的方法是通过使用 Cglib 生成。所以说
有使用 Cglib 的项目是有可能出现方法区溢出异常的,比如 Spring 的 Aop, 如果大量的增强类,会产生很多的类,方法区的大小不够就会产生异常。
如果使用-XX:PermSize=1M -XX:MaxPermSize=1M
, 要确保 jvm 版本低于 8,因为 Java1.8 移除了永久代,类信息放在了 Meta Space。
控制 Meta space 用 -XX:MetaspaceSize
和 -XX:MaxMetaspaceSize
参数:-XX=10m -XX=10m -Xmx20m -Xms10m
while (true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestMain.class);
enhancer.setUseCache(false);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, objects));
enhancer.create();
}
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
直接内存溢出#
直接内存溢出通常出现在 Nio 出现的地方,比如 Netty。可以通过 unsafe 方法直接请求系统内存来模拟直接内存溢出。
参数: -XX=10M -XX:+HeapDumpOnOutOfMemoryError
Field field = Unsafe.class.getDeclaredFields()[0];
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
while (true) {
unsafe.allocateMemory(1 * 1024 * 1024);
}
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)