OOM是什么?英文全称为 OutOfMemoryError(内存溢出错误)。当程序发生OOM时,怎样去定位导致异常的代码还是挺麻烦的。
要查抄OOM发生的原因,首先必要了解各种OOM情况下会报的异常信息。如许能缩小排查范围,再联合异常堆栈、heapDump文件、JVM分析工具和业务代码来判断具体是哪些代码导致的OOM。笔者在此测试并记载以下几种OOM情况。
环境准备
- jdk1.8(HotSpot捏造机)
- windows操作系统
- idea开发工具
在idea上举行测试时,必要了解idea实行测试用比方何设置捏造机参数(VM options)。如下图所示:
- 单击main方法的启动图标,选择修改运行配置
- 打开Add VM options,将JVM参数填在图示VM options处
堆溢出
Java堆是用来存储对象实例的,只要不停的创建对象,并包管对象不被GC接纳掉,那么当对象占用的内存达到了最大堆内存限定,无法再申请到新的内存空间时,就会导致OOM。要让对象不被接纳就必要包管GC Roots引用链可以到达该对象,此处采用了List来保持对对象的引用。并且设置参数-XX:+HeapDumpOnOutOfMemoryError打印OOM发生时的堆内存状态。代码如下:- /**
- * VM options: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
- * @author yywf
- * @date 2024/4/11
- */
- public class HeapOOMTest {
- public static void main(String[] args) {
- List<Object> list = new LinkedList<>();
- while (true) {
- list.add(new Object());
- }
- }
- }
复制代码 实行效果
提示信息为GC overhead limit exceeded。
使用JProfiler打开heapDump文件,可以看到启动类加载器中的java.util.LinkedList占用了92.3%的堆内存
字符串常量池溢出
通过String.intern()这个native方法将字符串添加到常量池中。
测试代码如下:- /**
- * VM options: -Xms2M -Xmx2M
- * @author yywf
- * @date 2024/4/11
- */
- public class StringConstantOOMTest {
- public static void main(String[] args) {
- List<String> list = new LinkedList<>();
- int i = 0;
- while (true) {
- list.add(String.valueOf(i++).intern());
- }
- }
- }
复制代码 实行效果
在jdk8中,字符串常量池已经移到了堆中。所以抛出的异常是堆内存溢出。
栈溢出
在JVM规范中,栈有捏造机栈和本地方法栈之分。但在现实的实现中,HotSpot捏造机是没有区分捏造机栈和本地方法栈的。所以对于HotSpot来说,-Xoss(设置本地方法栈大小)参数是无效的,栈容量只能通过-Xss参数设置。
栈深度造成的溢出
在JVM规范中,如果线程请求的栈深度大于捏造机所允许的最大深度,将抛出StackOverflowError异常。测试代码如下:- /**
- * VM options: -Xss128k
- * @author yywf
- * @date 2024/4/11
- */
- public class StackOOMTest {
- private int stackLength = 1;
- public void stackDeep() {
- stackLength++;
- stackDeep();
- }
- public static void main(String[] args) {
- StackOOMTest test = new StackOOMTest();
- try {
- test.stackDeep();
- } catch (Throwable e) {
- System.out.println("栈深度:" + test.stackLength);
- throw e;
- }
- }
- }
复制代码 实行效果
创建线程造成的内存溢出
另一种情况,呆板的RAM内存是固定的,如果不思量其他程序占用内存,那么RAM就由堆、方法区、程序计数器、捏造机栈和本地方法栈瓜分。通过不停的创建线程占满RAM的内存,会导致什么情况呢?测试代码:- /**
- * VM options: -Xss10M
- * @author yywf
- * @date 2024/4/11
- */
- public class CreateThreadOOMTest {
- public void stackOOMByThread() {
- while (true) {
- Thread thread = new Thread(() -> {
- while (true) {
- try {
- Thread.sleep(60000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
- }
- }
- public static void main(String[] args) {
- CreateThreadOOMTest test = new CreateThreadOOMTest();
- test.stackOOMByThread();
- }
- }
复制代码 这里把栈的大小设置为了10M,也就是说创建一个线程最少必要10M的内存。可以更快的出现效果。
实行效果
抛出的是OutOfMemoryError。慎用慎用慎用,重要的事情说三遍,本人在测试的时候电脑死机了一会。得亏在线程的run方法中让线程睡眠了,否则cpu+内存双双阵亡。
方法区溢出
方法区大小在jdk1.7(包含)以前版本是通过-XX ermSize和-XX:MaxPermSize来设置的。在jdk8的实现叫做元空间(metaspace),通过-XX:MetaspaceSize=10M和-XX:MaxMetaspaceSize=10M来设置其大小。
方法区存放的是类的信息,所以在运行时不停创建类就行。这里使用CGLib动态代理来生成类,可以添加以下maven依赖来使用CGLib:- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>5.3.13</version>
- </dependency>
复制代码 测试代码如下:- /**
- * VM options: -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
- * @author yywf
- * @date 2024/4/11
- */
- public class MetaSpaceOomTest {
- public static void main(String[] args) {
- while (true) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(Object.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();
- }
- }
- }
复制代码 实行效果
本机直接内存溢出
通过-XX:MaxDirectMemorySize=10M参数设置能申请的DirectMemory大小。如果不设置则默以为java堆的最大值。通过反射获取Unsafe实例,使用其来申请DirectMemory内存。
测试代码如下:- /**
- * VM options: -Xmx10M -XX:MaxDirectMemorySize=10M
- * @author yywf
- * @date 2024/4/11
- */
- public class DirectOOMTest {
- public static void main(String[] args) throws IllegalAccessException {
- Field unsafeField = Unsafe.class.getDeclaredFields()[0];
- unsafeField.setAccessible(true);
- Unsafe unsafe = (Unsafe) unsafeField.get(null);
- while (true) {
- unsafe.allocateMemory(1048576);
- }
- }
- }
复制代码 实行效果
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |