美文网首页程序园Java BlogJava 杂谈
JVM学习笔记二【异常实战】

JVM学习笔记二【异常实战】

作者: 爪哇部落格 | 来源:发表于2019-05-05 22:03 被阅读1次

\color{red}{码字不易,欢迎大家转载,烦请注明出处;谢谢配合}

演示环境如下:

  • JDK1.8

前文我们针对JVM运行时区域划分做了介绍,明确了各区域的作用,以及可能出现的异常,烦请思考以下问题:

什么样的代码会让指定区域反生OutOfMemoryError(OOME)或者StackOverflowError(SOE)呢?

这个问题你也许会感到困惑,为什么要去了解会出现OOME或者SOE的代码?实际上我们只有对OOME或者SOE有了清楚的认识,才能合理的避免问题的产生,以及发生问题时能够可以有效的解决。

Java堆:OutOfMemoryError

首先在“构建”Java堆的OutOfMemoryError时,我们应对Java堆有充足的认识,只有知道堆的作用,才能知道为何Java堆会发生OutOfMemoryError;Java堆中存储的是对象的实例,这也是我们的模拟此问题的出发点,“构建”Java堆的OutOfMemoryError参考以下代码示例:

代码调试

调整虚拟机启动参数:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError

package sunce.xin.education.jvm;

import java.util.ArrayList;
import java.util.List;

/**
 * Java堆OutOfMemoryError
 * @author lowrie
 * @date 2019-05-05
 */
public class HeapErrorTest {

    public static void main(String[] args) {
        List<HeapErrorTest> list = new ArrayList<>();
        while (true) {
            list.add(new HeapErrorTest());
        }
    }
}

Error输出

Java堆

dump文件分析

在发生OutOfMemoryError时我们可以导出dump文件,利用内存分析工具来定位问题产生的原因,笔者常用的内存分析工具是Jprofiler,如下是通过Jprofiler分析以上代码dump的示例:

dump

Java虚拟机栈:StackOverflowError

Java虚拟机栈是由线程创建用于描述Java方法的执行,栈的大小越大可执行的方法深度越深,为了方便“构建”StackOverflowError,我们修改启动参数如下:

代码调试

调整虚拟机启动参数:-Xss256k

package sunce.xin.education.jvm;

/**
 * 栈StackOverflowError
 * @author lowrie
 * @date 2019-05-05
 */
public class StackErrorTest {

    private int length = 0;

    public static void main(String[] args) {
        StackErrorTest object = new StackErrorTest();
        try {
            object.callMethod();
        } catch (Throwable e) {
            System.out.println("stack deep length:" + object.length);
            e.printStackTrace();
        }
    }

    public void callMethod() {
        while (true) {
            length += 1;
            callMethod();
        }
    }
}

Error输出

stack

实验结果表明,无论是栈帧太大,或者栈内存空间过小,当无法分配内存时,虚拟机都会抛出StackOverflowError.

Java虚拟机栈:OutOfMemoryError

我们知道当虚拟机扩展栈时,如果无法申请到足够的内存空间,则会抛出OutOfMemoryError;操作系统分配给每个进程的内存空间是有限的,假如你的操作系统内存只有2g,减去分配给堆的Xmx(最大堆大小),再减去分配给元数据的MaxMetaspaceSize/MaxPermSize,程序计数器占用的空间很小可以忽略,剩余的就被Java虚拟机栈和本地方法栈瓜分,所以每个栈空间分配的越大,所能创建的线程数越少。

代码调试

调整虚拟机启动参数:-Xss2m

package sunce.xin.education.jvm;

/**
 * 栈空间无法分配导致的内存溢出
 * 虚拟机参数 -Xss2m
 *
 * @author lowrie
 * @date 2019-05-06
 */
public class StackCauseOutOfMemoryErrorTest {

    private void stackLeak() {
        while (true) {
        }
    }

    private void currentTest() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    stackLeak();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        StackCauseOutOfMemoryErrorTest test = new StackCauseOutOfMemoryErrorTest();
        test.currentTest();
    }
}

Error输出

stack

方法区:OutOfMemoryError

我们知道方法区(元数据区)是用来保存类的描述信息,以及常量池等信息;所以“构建”此区域的OutOfMemoryError,出发点也是针对其保存的信息;我们通过以下实例来构建:

代码调试

调整虚拟机启动参数:
JDK1.8 -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
JDK1.7 -XX:PermSize=10m -XX:MaxPermSize=10m

package sunce.xin.education.jvm;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Meta/Method Area OutOfMemoryError
 * JDK1.8 -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
 *
 * @author lowrie
 * @date 2019-05-05
 */
public class MetaErrorTest {

    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MetaErrorTest.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, objects);
                }
            });
            enhancer.create();
        }
    }

}

Error输出

Metaspace

发现过程

添加-verbose:class参数,观察class的加载过程
-verbose:class -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

异常输出如下:


类加载过程

附录 JVM常用参数

参数 示例 说明
-Xsssize -Xss1m 设置线程栈大小
-Xmssize -Xms1m 设置堆初始大小
-Xmxsize -Xmx1m 设置堆最大大小
-Xmnsize -Xmn1m 设置年轻代最大大小
-XX:NewSize=size -XX:NewSize=10m 设置年轻代初始大小
-XX:MetaspaceSize=size -XX:MetaspaceSize=10m 设置元数据区大小
-XX:MaxMetaspaceSize=size -XX:MaxMetaspaceSize=10m 设置最大元数据区大小
-verbose:class -verbose:class 显示类加载信息
-verbose:gc -verbose:gc 显示GC信息

参考oracle官方参数

相关文章

  • JVM学习笔记二【异常实战】

    演示环境如下: JDK1.8 前文我们针对JVM运行时区域划分做了介绍,明确了各区域的作用,以及可能出现的异常,烦...

  • 2019实战第二期-异常读书打卡

    -----学习《Python基础教程第3版》读书笔记----- 2019实战第二期-异常读书打卡 异常是什么 使用...

  • 【并发编程】- Java内存模型-Happens-Before规

    学习王宝令老师的《Java并发编程实战》记录的学习笔记。 Java内存模型规范了JVM如何提供按需禁用缓存和编译优...

  • JVM 调优之 Eclipse 启动调优实战

    本文是我12年在学习《深入理解Java虚拟机:JVM高级特性与最佳实践》时,做的一个 JVM 简单调优实战笔记,版...

  • Java开发

    JVM 内存溢出实例 - 实战 JVM(二) 介绍 JVM 内存溢出产生情况分析Java - 注解详解 详细介绍 ...

  • JVM学习笔记(3)-垃圾收集算法

    JVM学习笔记(1)-内存管理机制 JVM学习笔记(2)-内存分配与回收 垃圾收集算法 JVM垃圾收集算法有四种:...

  • 谈谈性能调优思路

    声明:本文为学习总结篇,参考资料见文末,如有侵权请联系作者,调优实践总结篇可参考以往文章:JVM学习笔记与调优实战...

  • 【JVM篇】【JVM的组成】

    【JAVA】【JVM篇】【JVM的组成】 来自二线的码农笔记,用自己的理解总结知识点,互相学习 1. JVM概念 ...

  • JVM学习笔记(二)

    一.JVM与程序的生命周期 Java虚拟机结束生命周期: ①执行了System.exit()方法。 ②程序正常执行...

  • Jvm学习笔记(二)

    Class类文件结构 无符号数:u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节 magic:...

网友评论

    本文标题:JVM学习笔记二【异常实战】

    本文链接:https://www.haomeiwen.com/subject/ghlkoqtx.html