在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区都会发生OOM异常的可能,本文通过几个例子来了解一下虚拟机常见的OOM异常。
本文的代码参考《深入理解Java虚拟机(第二版)》
Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免避免垃圾回收清除对象,那么这些对象达到最大堆的容量限制之后就会产生内存溢出异常。
-Xms20m //JVM初始分配的内存20m
-Xmx20m //JVM最大可用内存为20m
-XX:+HeapDumpOnOutOfMemoryError //当JVM发生OOM时,自动生成DUMP文件
-XX:HeapDumpPath=/Users/wangchengming/Desktop/ //生成DUMP文件的路径,方便后面进行分析
如果不加-XX:HeapDumpPath
和-XX:+HeapDumpOnOutOfMemoryError
参数的话,也可以通过jps
命令找到pid
,如下:
然后使用jmap -dump:format=b,file=serviceDump.dat 2513
命令手打导出dump文件,如下:
public class OOMTest {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> oomObjects = new ArrayList<>();
while (true) {
oomObjects.add(new OOMObject());
}
}
}
java.lang.OutOfMemoryError: Java heap space
Dumping heap to /Users/wangchengming/Desktop/java_pid2493.hprof ...
Heap dump file created [27779830 bytes in 0.088 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at OOMTest.main(OOMTest.java:16)
Process finished with exit code 1
可以看出一共创建了810326个对象,占用了99.4%的内存,发现内存被撑爆了。
堆内存溢出
。这个时候可以定位到代码:OOMTest$OOMOject
,发现是死循环导致的,修改代码之后就不会出现堆内存溢出了通常情况下栈容量是由-Xss
参数设定。关于虚拟机栈和本地方法栈,在Java虚拟机中描述了两种异常:
接下来演示一波StackOverflowError
-Xss160k
,最小是160k
,默认1M
private int stackLength = 1;
/**
* 递归
*/
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
OOMTest oomTest = new OOMTest();
try {
oomTest.stackLeak();
} catch (Throwable e) {
System.out.println("stack length : " + oomTest.stackLength);
throw e;
}
}
StackOverflowError
stack length : 773
Exception in thread "main" java.lang.StackOverflowError
at OOMTest.stackLeak(OOMTest.java:23)
at OOMTest.stackLeak(OOMTest.java:24)
at OOMTest.stackLeak(OOMTest.java:24)
at OOMTest.stackLeak(OOMTest.java:24)
接下来演示一波OOM
-Xss2M
private void dontStop() {
while (true) {
}
}
private void stackLeakByThread() {
while (true) {
Thread thread = new Thread(this::dontStop);
thread.start();
}
}
public static void main(String[] args) {
OOMTest oomTest = new OOMTest();
oomTest.stackLeakByThread();
}
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
-Xss
降低的每个线程栈大小的容量方法区,(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时产生大量的类,会填满方法区,造成溢出。
-XX:MetaspaceSize=10M
和-XX:MaxMetaspaceSize=10M
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:538)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:131)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572)
at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:387)
at OOMTest.main(OOMTest.java:31)
Caused by: java.lang.OutOfMemoryError: Metaspace
,然后检查代码是否应用了大量的代理,发现确实是应用了代理。直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。但是,这部分内存也被频繁地使用,而且也可能导致OOM。
在JDK1.4 中新加入了NIO(New Input/Output)类,它可以使用 native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
DirectMemory容量可以通过-XX:MaxDirectMemorySize
指定,如果不指定则默认和Java堆最大值(-Xmx指定)一致。
-Xmx256m
和-XX:MaxDirectMemorySize=100M
public static void main(String[] args) throws IllegalAccessException, InterruptedException {
//分配128MB直接内存
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
TimeUnit.SECONDS.sleep(10);
System.out.println("ok");
}
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 OOMTest.main(OOMTest.java:85)
-Xmx
,-XX:MaxDirectMemorySize
是否合理。通过以上的分析,一共得出以下几种常见OOM异常,希望可以帮到各位小伙伴
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: unable to create new native thread
java.lang.OutOfMemoryError: Metaspace
java.lang.OutOfMemoryError: Direct buffer memory
本文地址:https://blog.csdn.net/wangchengming1/article/details/107343013
如对本文有疑问, 点击进行留言回复!!
NullPointerException: Attempt to invoke virtual method ‘android.content.res.XmlResourceParser androi
关于启动appium-desktop,报错:Cannot extract apk info using apkanalyzer. Falling back to aapt. Original ....
Gradle 发布共享库——如何通过Gradle发布Android依赖库(aar)到 jitpack 公共仓库
Gradle 发布共享库——如何通过Gradle发布java依赖库(jar)到 jitpack 公共仓库(—)
网友评论