对于JVM来说,因为划分有固定的区域来执行字节码文件,无外乎,出问题的,也就是按照对应分分区会有常见的OOM问题。
栈
对于栈来说,栈的主要作用就是用于方法的执行,方法调用入栈、方法调出出栈。但是如果我们写一个递归的程序,如果层级太深,那么就会导致出现栈深度超过系统限制。
StackOverflowError
栈深度一般默认是512K。所以在实际的生产环境中,使用递归要慎重。
public static void main(String[] args) {
f();
}
private static void f () {
f();
}
Exception in thread "main" java.lang.StackOverflowError
at oom.JavaStackOverFlowerErrorDemo.f(JavaStackOverFlowerErrorDemo.java:14)
堆
堆空间主要存储的是对象,如果创建对象超过了所分配对象的空间。那么就会出现java heap space
Java. heap space
// -Xms10m -Xmx10m
public class JavaHeapSpaceDemo {
public static void main(String[] args) {
// 创建对象 12MB对象
byte [] bytes = new byte[12 * 1024 * 1024];
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at oom.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:13)
# GC overhead limit exceeded
Gc执行有一定的限制,如果超过了98%的时间用来做GC,并且回收了不到2%的堆内存,说白了就是一定的GC时间,回收内存过小,就会出现。
如果不抛出这个异常,那么堆空间越来越小,CPU使用率100%,但是GC没有效果。
/*
* JVM参数配置: -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
*
*/
public class GcOverheadLimitDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
while (true) {
list.add(String.valueOf(++i).intern());
}
}
}
[Full GC (Allocation Failure) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7164K->7164K(7168K)] 9212K->9212K(9728K), [Metaspace: 3263K->3263K(1056768K)], 0.0693555 secs] [Times: user=0.42 sys=0.01, real=0.07 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7165K->7164K(7168K)] 9213K->9212K(9728K), [Metaspace: 3264K->3264K(1056768K)], 0.0564705 secs] [Times: user=0.32 sys=0.00, real=0.05 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->0K(2560K)] [ParOldGen: 7167K->394K(7168K)] 9215K->394K(9728K), [Metaspace: 3268K->3268K(1056768K)], 0.0044834 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 2560K, used 66K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 2048K, 3% used [0x00000007bfd00000,0x00000007bfd10bb8,0x00000007bff00000)
from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
ParOldGen total 7168K, used 394K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
object space 7168K, 5% used [0x00000007bf600000,0x00000007bf662be0,0x00000007bfd00000)
Metaspace used 3295K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 363K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
堆外内存
Direct buffer memory
写NIO程序的时候经常会使用ByteBuffer来读取或写入数据,这是一种基于通道(Channel) 与 缓冲区(Buffer)的I/O方式,它可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
ByteBuffer.allocate(capability):第一种方式是分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢
ByteBuffer.allocteDirect(capability):第二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存的拷贝,所以速度相对较快
但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象就不会被回收,这时候怼内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,那么程序就奔溃了。
一句话说:本地内存不足,但是堆内存充足的时候,就会出现这个问题
// -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
public static void main(String[] args) {
// 分配6M 堆外内存
ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at oom.DirectMemorySizeDemo.main(DirectMemorySizeDemo.java:13)
这时候就会出现异常,堆外内存不够用。
本地方法栈
unable to create new native thread
这个异常其实就是,在应用进程内创建的线程超过进程限制格式,默认是1024个。
【复盘】记录一次JVM 异常问题 java.lang.OutOfMemoryError: unable to create new native thread
元空间
java.lang.OutOfMemoryError:Metaspace
元空间就是我们的方法区,存放的是类模板,类信息,常量池等.一般出现这个错误,要么就是框架动态生成的类太多,或者 引用的 jar 包加载了很多 class。
/**
-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
* @author qxlx
* @date 2024/2/25 22:00
*/
public class MetaspaceOutOfMemoryDemo {
static class OOMTest {
}
public static void main(String[] args) {
int i = 0;
try {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,args);
}
});
}
} catch (Exception e) {
System.out.println("i="+i);
e.printStackTrace();
}
}
}
java.lang.OutOfMemoryError:Metaspace