一、引言
从事 java 开发的小伙伴在平时的开发工作中,应该会遇见各式各样的异样和谬误,在理论工作中积攒的异样或者谬误越多,趟过的坑越多,就会使咱们编码更加的强壮,就会本能地避开很多重大的坑。以下介绍几个 Java 虚拟机常见内存溢出谬误。以此警示,防止生产血案。
二、模仿 Java 虚拟机常见内存溢出谬误
1、内存溢出之栈溢出谬误
package com.jayway.oom;
/**
* 栈溢出谬误
* 虚拟机参数:-Xms10m -Xmx10m
* 抛出异样:Exception in thread "main" java.lang.StackOverflowError
*/
public class StackOverflowErrorDemo {public static void main(String[] args) {stackOverflowError();
}
private static void stackOverflowError() {stackOverflowError();
}
}
2、内存溢出之堆溢出谬误
package com.jayway.oom;
import java.util.Random;
/**
* 堆溢出谬误
* 虚拟机参数:-Xmx10m -Xms10m
* 抛出异样:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
*/
public class JavaHeapSpaceErrorDemo {public static void main(String[] args) {
String temp = "java";
// 一直地在堆中开拓空间,创建对象,撑爆堆内存
while (true) {temp += temp + new Random().nextInt(111111111) + new Random().nextInt(222222222);
temp.intern();}
}
}
3、内存溢出之 GC 超过执行限度谬误
package com.jayway.oom;
import java.util.ArrayList;
import java.util.List;
/**
* GC 超过执行限度谬误
* 虚拟机参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
* * 抛出异样:Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
* * 导致起因:GC 回收工夫过长会抛出 OutOfMemoryError,何为过长,即超过 98% 的 cpu 工夫用来做 GC 垃圾回收
* 然而回收成果甚微,仅仅只有 2% 的 CPU 工夫用来用户程序的工作,这种状态是很蹩脚的,程序在一直地 GC
* 造成恶性循环,CPU 的使用率始终是满负荷的,正经活却没有干,这种状况虚拟机只好抛出谬误来终止程序的执行
*
* 一直地 Full GC,事倍功微
* [Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7167K->7161K(7168K)] 9215K->9209K(9728K), [Metaspace: 3529K->3529K(1056768K)], 0.0291829 secs] [Times: user=0.08 sys=0.02, real=0.03 secs]
*/
public class GCOverheadErrorDemo {public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
try {while (true) {list.add(String.valueOf(++i).intern());
}
} catch (Throwable e) {System.out.println("*****************i:" + i);
e.printStackTrace();
throw e;
}
}
}
4、内存溢出之间接内存溢出谬误
package com.jayway.oom;
import java.nio.ByteBuffer;
/**
* 间接内存溢出谬误
* 抛出异样:Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
* * 配置虚拟机参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
* * 导致起因:通常 NIO 程序常常应用 ByteBuffer 来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的 IO 形式,* 它能够应用 Native 函数库间接调配堆外内存,而后通过一个存储在 java 堆外面的 DirectByteBuffer 对象作为这块内存的援用,* 这样能子一些场景中显著进步性能,因为防止了在 Java 堆和 Native 内存中来回复制数据。*
* ByteBuffer.allocate(capability): 调配 JVM 堆内存,数据 GC 的管辖范畴,因为须要拷贝所以速度绝对较慢
*
* ByteBuffer.allocate(capability): 调配 OS 本地内存,不属于 GC 管辖范畴,因为不须要内存拷贝,所以速度绝对较快。*
* 然而如果一直调配本地内存,堆内存很少应用,那么 JVM 就不须要执行 GC,DirectByteBuffer 对象就不会被回收,此时如果持续调配堆外内存,* 可能堆外内存曾经被耗光了无奈持续调配,此时程序就会抛出 OutOfMemoryError,间接解体。*
*/
public class DirectBufferMemoryErrorDemo {public static void main(String[] args) {
// 默认 JVM 配置的最大间接内存是总物理内存的四分之一
long maxDirectMemory = sun.misc.VM.maxDirectMemory() / 1024 / 1024;
System.out.println("配置的 maxDirectMemory:" + maxDirectMemory + "MB");
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
5、内存溢出之无奈创立新的本地线程
package com.jayway.oom;
/**
* 内存溢出之无奈创立新的本地线程
* 抛出异样:java.lang.OutOfMemoryError: unable to create new native thread
* * 形容:* 高并发申请服务器时,经常出现 java.lang.OutOfMemoryError: unable to create new native thread
* native thread 异样与对应的平台无关
*
* 导致起因:* 1、应用程序创立了太多线程了,一个利用过程创立的线程数超过零碎承载极限。* 2、操作系统并不容许你的利用过程创立这么多的线程,linux 零碎默认容许单个过程能够创立的线程数是 1024 个
*
* 解决办法:* 1、想方法升高利用过程创立的线程数量,* 2、如果应用程序的确须要这么多线程,超过了 linux 零碎的默认 1024 个限度,能够通过批改 linux 服务器配置,进步这个阈值。*
*/
public class UnableCreateNativeThreadErrorDemo {public static void main(String[] args) {for (int i = 0; true; i++) {System.out.println("***************i:" + i);
// 一直得创立新线程,直到超过操作系统容许利用过程创立线程的极限
new Thread(() -> {
try {Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {e.printStackTrace();
}
}).start();}
}
}
6、内存溢出之元空间溢出谬误
package com.jayway.oom;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 元空间溢出谬误
* 抛出异样:java.lang.OutOfMemoryError: Metaspace
* * 设置虚拟机参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
* * 形容:Java8 及当前的版本应用 Metaspace 来代替了永恒代。metaspace 是办法区在 HotSpot 中的实现,它与长久代最大的区别在于
* Metaspace 并不在虚拟机内存中而是在本地内存中。*
* 元空间存储了以下信息:* 1、虚拟机加载的类信息
* 2、常量池
* 3、动态变量
* 4、即时编译后的代码
*
*/
public class MetaspaceErrorDemo {static class OOMTest {}
public static void main(String[] args) {
int count = 0;
try {
//cglib 一直创立类,模仿 Metaspace 空间溢出,咱们一直生成类往元空间中灌,超过元空间大小后就会抛出元空间移除的谬误
while (true) {
count++;
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);
}
});
enhancer.create();}
} catch (Throwable e) {System.out.println("************ 多少次后产生了异样:" + count);
e.printStackTrace();}
}
}