放假这几天,温习了深刻理解Java虚拟机的第二章, 整理了JVM发生OOM异常的几种状况,并分析缘由以及解决方案,但愿对你们有帮助。html
Java堆用于存储对象实例,只要不断地建立对象,而且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。java
java.lang.OutOfMemoryError: Java heap space
复制代码
package oom;
import java.util.ArrayList;
import java.util.List;
/**
* JVM配置参数
* -Xms20m JVM初始分配的内存20m
* -Xmx20m JVM最大可用内存为20m
* -XX:+HeapDumpOnOutOfMemoryError 当JVM发生OOM时,自动生成DUMP文件
* -XX:HeapDumpPath=/Users/weihuaxiao/Desktop/dump/ 生成DUMP文件的路径
*/
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
//在堆中无限建立对象
while (true) {
list.add(new OOMObject());
}
}
}
复制代码
按照前面的排查解决方案,咱们来一波分析。spring
1.查找报错关键信息bash
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
复制代码
2. 使用内存映像分析工具Jprofiler分析产生的堆储存快照jsp
由图可得,OOMObject这个类建立了810326个实例,是属于内存溢出,这时候先定位到对应代码,发现死循环致使的,修复便可。函数
关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:工具
package oom;
/**
* -Xss2M
*/
public class JavaVMStackOOM {
private void dontStop(){
while(true){
}
}
public void stackLeakByThread(){
while(true){
Thread thread = new Thread(new Runnable(){
public void run() {
dontStop();
}
});
thread.start();}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
复制代码
1.查找报错关键信息post
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
复制代码
2.肯定是建立线程致使的栈溢出OOM性能
Thread thread = new Thread(new Runnable(){
public void run() {
dontStop();
}
});
复制代码
3.排查代码,肯定是否显示使用死循环建立线程,或者隐式调用第三方接口建立线程(以前公司,调用腾讯云第三方接口,上传图片,遇到这个问题)学习
方法区,(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时产生大量的类,会填满方法区,形成溢出。
package oom;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* jdk8以上的话,
* 虚拟机参数:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
*/
public class JavaMethodAreaOOM {
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 {
}
}
复制代码
1.查找报错关键信息
Caused by: java.lang.OutOfMemoryError: Metaspace
复制代码
2.检查JVM元空间设置参数是否太小
-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
复制代码
3. 检查对应代码,是否使用CGLib生成了大量的代理类
while (true) {
...
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
复制代码
直接内存并非虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。可是,这部份内存也被频繁地使用,并且也可能致使OOM。
在JDK1.4 中新加入了NIO(New Input/Output)类,它可使用 native 函数库直接分配堆外内存,而后经过一个存储在Java堆中的 DirectByteBuffer 对象做为这块内存的引用进行操做。这样能在一些场景中显著提升性能,由于避免了在 Java 堆和 Native 堆中来回复制数据。
package oom;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
/**
* -Xmx256m -XX:MaxDirectMemorySize=100M
*/
public class DirectByteBufferTest {
public static void main(String[] args) throws InterruptedException{
//分配128MB直接内存
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
TimeUnit.SECONDS.sleep(10);
System.out.println("ok");
}
}
复制代码
ByteBuffer分配128MB直接内存,而JVM参数-XX:MaxDirectMemorySize=100M指定最大是100M,所以发生直接内存溢出。
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
复制代码
package oom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* JVm参数 -Xmx8m -Xms8m
*/
public class GCoverheadTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
});
}
}
}
复制代码
本文介绍了如下几种常见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
java.lang.OutOfMemoryError: GC overhead limit exceeded
复制代码
但愿你们遇到OOM异常时,对症下药,顺利解决问题。同时,若是有哪里写得不对,欢迎指出,感激涕零。