HotSpot虚拟机OutOfMemoryError异常

news/2024/5/20 9:54:37 标签: jvm, , 元空间

目录

一、JVM内存区域

二、中对象

1. 对象的创建

2. 对象的内存布局

3. 对象的访问定位

三、OOM异常

1. OOM异常测试

2. 栈SOF异常测试

        1):栈容量过小

        2):大量本地变量

3. 常量池OOM异常测试

4. 方法区测试

5. 外内存测试

四、参考资料


一、JVM内存区域

        HotSpot虚拟机把内存分为不同的数据区域,有的区域随JVM启动而一直存在(线程共享区域),有的区域随用户线程启动和结束而建立和销毁(线程私有区域),如下图所示。

        下图所示是线程共享的区域,其中区由参数-Xms和-Xmx决定大小,若是两参数一致,则不能动态扩展,否则可动态扩展;JDK7版本方法区内的常量池、静态变量移至中;JDK8使用元空间(Metaspace)代替永久代,主要存储类信息

        下图所示是线程私有的区域,其中程序计数器是唯一一个没有OOM异常的区域;虚拟机栈由参数-Xss决定栈容量,不能动态扩展

        下图所示是外内存,由参数-XX:MaxDirectMemorySize决定大小,没有配置该参数时,默认大小与-Xmx大小一致。需要注意:直接内存不能主动触发GC回收,只有执行Full GC时,顺带回收外内存

二、中对象

1. 对象的创建

        开发人员创建对象通常仅仅是个new关键字,而JVM中创建对象的过程如下图所示。

        一般来说,new指令后面若是紧跟着invokespecial指令,则会执行该Class的<init>()方法,这样一个真正可用的对象才完整构造出来。如下图所示是IDEA插件jclasslib查看字节码,new HashSet<>();会同时生成new和invokespecial指令,invokespecial指令会执行HashSet类的<init>()方法完成对象构造

2. 对象的内存布局

        实例数据的存储顺序会受到虚拟机分配策略参数-XX:FieldsAllocationStyle参数和定义顺序的影响,其默认顺序是longs/doubles、ints、shorts/chars、bytes/booleans、oops。分配顺序原则:相同宽度的字段放在一起,父类字段放在子类字段前面

3. 对象的访问定位

        中对象是通过虚拟机栈中的reference数据来访问,其访问方式如下图所示。

        两种访问方式:句柄、直接指针,如下图所示。 

三、OOM异常

1. OOM异常测试

        JVM配置:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError,如下代码所示。

package com.cmmon.instance.oom;

import com.google.common.collect.Lists;

import java.util.List;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试OOM
 * @date 2023/5/9 15:48
 **/
public class HeapOom {

    static class OOMObject { }

    /**
     * 大小设置,报出:java.lang.OutOfMemoryError
     * -Xms:JVM启动时初始化大小,默认是物理内存的1/64
     * -Xmx:初最大占用内存大小,默认是物理内存的1/4
     * -XX:+HeapDumpOnOutOfMemoryError:JVM异常退出时,生成Dump转储快照,进行分析
     * VM参数:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
     */
    public static void main(String[] args) {
        List<HeapOom.OOMObject> list = Lists.newArrayList();

        while (true) {
            list.add(new HeapOom.OOMObject());
        }
    }

}

        测试结果如下:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid13308.hprof ...
Heap dump file created [30014384 bytes in 0.237 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 com.cmmon.instance.oom.HeapOom.main(HeapOom.java:28)

         java_pid13308.hprof转储文件分析如下。文件详细参数,参考:https://www.cnblogs.com/duanxz/p/5230856.html

2. 栈SOF异常测试

        HotSpot虚拟机栈,不支持动态扩展,所以导致栈java.lang.StackOverflowError异常,有两种情况:

  • -Xss参数配置栈容量;
  • 方法中定义大量的变量,导致栈帧中本地变量表大。

        1):栈容量过小

        JVM配置:-Xss108k,如下代码所示。

package com.cmmon.instance.oom;

import lombok.Data;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试栈 - 设置栈容量大小
 * @date 2023/5/9 16:24
 **/
public class JVMStackSOF {

    @Data
    public static class JavaVMStackSOF {
        public int stackLength = 1;

        public void stackLeak() {
            stackLength++;
            // 递归调用
            stackLeak();
        }

    }

    /**
     * 栈容量大小,报出:java.lang.StackOverflowError
     * VM参数:-Xss108k
     * step1:设置-Xss5k,会提示信息"The stack size specified is too small, Specify at least 108k"
     *        说明:当前java的JVM版本与OS,栈容量最低是108k
     * step2:设置-Xss大于等于108k
     */
    public static void main(String[] args) {
        JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();

        try {
            javaVMStackSOF.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length: " + javaVMStackSOF.getStackLength());
            throw e;
        }
    }

}

        测试结果如下:

stack length: 991
Exception in thread "main" java.lang.StackOverflowError
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:18)
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:20)
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:20)
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:20)
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:20)
	at com.cmmon.instance.oom.JVMStackSOF$JavaVMStackSOF.stackLeak(JVMStackSOF.java:20)
    ......

        2):大量本地变量

        JVM配置:无,如下代码所示。

package com.cmmon.instance.oom;

import lombok.Data;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试栈 - 大量本地变量
 * @date 2023/5/9 16:48
 **/
public class JVMStackSOF2 {

    @Data
    public static class JavaVMStackSOF2 {
        public int stackLength = 1;

        public void stackLeak() {
            // 定义大量变量
            long useData0, useData1, useData2, useData3, useData4, useData5, useData6, useData7, useData8, useData9,
                    useData10, useData11, useData12, useData13, useData14, useData15, useData16, useData17, useData18, useData19,
                    useData20, useData21, useData22, useData23, useData24, useData25, useData26, useData27, useData28, useData29,
                    useData30, useData31, useData32, useData33, useData34, useData35, useData36, useData37, useData38, useData39,
                    useData40, useData41, useData42, useData43, useData44, useData45, useData46, useData47, useData48, useData49,
                    useData50, useData51, useData52, useData53, useData54, useData55, useData56, useData57, useData58, useData59,
                    useData60, useData61, useData62, useData63, useData64, useData65, useData66, useData67, useData68, useData69,
                    useData70, useData71, useData72, useData73, useData74, useData75, useData76, useData77, useData78, useData79,
                    useData80, useData81, useData82, useData83, useData84, useData85, useData86, useData87, useData88, useData89,
                    useData90, useData91, useData92, useData93, useData94, useData95, useData96, useData97, useData98, useData99;

            stackLength++;
            // 递归调用
            stackLeak();

            useData0 = useData1 = useData2 = useData3 = useData4 = useData5 = useData6 = useData7 = useData8 = useData9
                    = useData10 = useData11 = useData12 = useData13 = useData14 = useData15 = useData16 = useData17 = useData18 = useData19
                    = useData20 = useData21 = useData22 = useData23 = useData24 = useData25 = useData26 = useData27 = useData28 = useData29
                    = useData30 = useData31 = useData32 = useData33 = useData34 = useData35 = useData36 = useData37 = useData38 = useData39
                    = useData40 = useData41 = useData42 = useData43 = useData44 = useData45 = useData46 = useData47 = useData48 = useData49
                    = useData50 = useData51 = useData52 = useData53 = useData54 = useData55 = useData56 = useData57 = useData58 = useData59
                    = useData60 = useData61 = useData62 = useData63 = useData64 = useData65 = useData66 = useData67 = useData68 = useData69
                    = useData70 = useData71 = useData72 = useData73 = useData74 = useData75 = useData76 = useData77 = useData78 = useData79
                    = useData80 = useData81 = useData82 = useData83 = useData84 = useData85 = useData86 = useData87 = useData88 = useData89
                    = useData90 = useData91 = useData92 = useData93 = useData94 = useData95 = useData96 = useData97 = useData98 = useData99 = 0;

        }

    }

    /**
     * 定义大量本地变量,方法栈中本地变量表大增,报出:java.lang.StackOverflowError
     * VM参数:无
     */
    public static void main(String[] args) {
        JVMStackSOF2.JavaVMStackSOF2 javaVMStackSOF2 = new JVMStackSOF2.JavaVMStackSOF2();

        try {
            javaVMStackSOF2.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length: " + javaVMStackSOF2.getStackLength());
            throw e;
        }
    }

}

        测试结果如下:

stack length: 596
Exception in thread "main" java.lang.StackOverflowError
	at com.cmmon.instance.oom.JVMStackSOF2$JavaVMStackSOF2.stackLeak(JVMStackSOF2.java:30)
	at com.cmmon.instance.oom.JVMStackSOF2$JavaVMStackSOF2.stackLeak(JVMStackSOF2.java:32)
	at com.cmmon.instance.oom.JVMStackSOF2$JavaVMStackSOF2.stackLeak(JVMStackSOF2.java:32)
	at com.cmmon.instance.oom.JVMStackSOF2$JavaVMStackSOF2.stackLeak(JVMStackSOF2.java:32)
	at com.cmmon.instance.oom.JVMStackSOF2$JavaVMStackSOF2.stackLeak(JVMStackSOF2.java:32)
    ......

3. 常量池OOM异常测试

        JVM:-Xmx6M,如下代码所示。需要注意:JDK8版本中,已经移除-XX:PermSize=6M和-XX:MaxPermSize=6M这两个参数,JDK6的常量池是放在方法区中,属永久代;而JDK8放在中,就一直运行下去

package com.cmmon.instance.oom;

import java.util.HashSet;
import java.util.Set;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试常量池OOM
 * @date 2023/5/9 16:54
 **/
public class ConstantPoolOOM {

    /**
     * 常量池容量限制,报出:java.lang.OutOfMemoryError
     * VM参数:-XX:PermSize=6M -XX:MaxPermSize=6M
     * Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=6M; support was removed in 8.0
     * Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=6M; support was removed in 8.0
     * 上述说明:JDK8版本中,已经移除这两个参数,JDK6的常量池是放在方法区中,属永久代;而JDK8放在中,就一直运行下去。
     *
     * VM参数:-Xmx6M
     * Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
     */
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();

        int i = 0;
        while (true) {
            set.add(String.valueOf(i++).intern());
        }
    }

}

        测试结果如下:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.util.HashMap.newNode(HashMap.java:1747)
	at java.util.HashMap.putVal(HashMap.java:631)
	at java.util.HashMap.put(HashMap.java:612)
	at java.util.HashSet.add(HashSet.java:220)
	at com.cmmon.instance.oom.ConstantPoolOOM.main(ConstantPoolOOM.java:29)

        测试JDK8版本常量池在中,测试代码如下。

/**
 * JDK8版本测试:常量池在中
 * String::intern
 * 字符串常量池已有该字符串:返回该字符串的引用
 * 字符串常量池没有该字符串:该字符串添加到常量池中,并返回该字符串的引用
 */
public void constantPoolOOM2() {
    String s1 = new StringBuilder("计算机").append("软件").toString();
    System.out.println(s1.intern());        // 计算机软件,属于第一次出现
    System.out.println(s1.intern() == s1);  // true

    // sun.misc.Version该类加载时,已经出现该“java”字符串
    String s2 = new StringBuilder("ja").append("va").toString();
    System.out.println(s2.intern());        // java,属于第二次出现
    System.out.println(s2.intern() == s2);  // false
}

4. 方法区测试

        JVM:-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=128M,如下代码所示。注意:JDK8版本方法区改为元空间,存储类信息;而常量、静态变量放入

package com.cmmon.instance.oom;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试方法区(元空间)
 * @date 2023/5/9 17:01
 **/
public class MethodAreaOOM {

    static class MethodAreaObject { }

    /**
     * 方法区内存溢出,报出:java.lang.OutOfMemoryError
     * -XX:MetaspaceSize设置元空间初始化大小,到达该值触发GC类型卸载,同时会对该值进行调整
     * -XX:MaxMetaspaceSize设置元空间最大值,默认-1,整个内存空间
     * -XX:MaxMetaspaceFreeRatio设置GC回收后控制元空间剩余容量最大百分比
     * -XX:MinMetaspaceFreeRatio设置GC回收后控制元空间剩余容量最小百分比
     *
     * VM参数:-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=128M
     * Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
     */
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MethodAreaOOM.MethodAreaObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invoke(o, new String[]{});
                }
            });
            enhancer.create();
        }
    }

}

        测试结果如下:

Exception in thread "main" org.springframework.cglib.core.CodeGenerationException: java.lang.OutOfMemoryError-->Metaspace
	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 com.cmmon.instance.oom.MethodAreaOOM.main(MethodAreaOOM.java:40)
Caused by: java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:535)
	... 7 more

5. 外内存测试

        JVM:-Xmx20M -XX:MaxDirectMemorySize=10M,如下代码所示。

package com.cmmon.instance.oom;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author tcm
 * @version 1.0.0
 * @description 测试直接内存(外内存)OOM
 * @date 2023/5/9 17:09
 **/
public class DirectMemoryOOM {

    /**
     * 直接内存溢出,报出:java.lang.OutOfMemoryError
     * -XX:MaxDirectMemorySize设置外内存最大值,若没有指定大小则与-Xmx大小一致
     *
     * VM参数:-Xmx20M -XX:MaxDirectMemorySize=10M
     */
    public static void main(String[] args) throws IllegalAccessException {
        int _1MB = 1024 * 1024;

        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);

        Unsafe unsafe = (Unsafe) unsafeField.get(null);

        while (true) {
            unsafe.allocateMemory(_1MB);
        }
    }

}

        测试结果如下:

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at com.cmmon.instance.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:30)

四、参考资料

JVM内存模型_小搬砖仔的博客-CSDN博客

https://www.cnblogs.com/duanxz/p/5230856.html

https://www.cnblogs.com/duanxz/p/8510623.html

为对象分配内存——TLAB_usetlab_chengqiuming的博客-CSDN博客

JVM调优常用参数_-xx:+printreferencegc_point-break的博客-CSDN博客


http://www.niftyadmin.cn/n/372980.html

相关文章

代码随想录补打卡 583两个字符串的删除操作 72 编辑距离

583两个字符串的删除操作 代码如下 func minDistance(word1 string, word2 string) int { dp[i][j]的含义是下标为i-1和下标为j-1的两个数组的最小操作数 dp : make([][]int,len(word1)1) for i,_ : range dp { dp[i] make([]int,len(word2)1) } for i : 0 ; i < len(…

TDengine 数据库SQL操作 | 建库、建表、数据读写

一、前文 TDengine 入门教程——导读 二、库操作 2.1 创建库 CREATE DATABASE test; BUFFER: 一个 VNODE 写入内存池大小&#xff0c;单位为 MB&#xff0c;默认为 96&#xff0c;最小为 3&#xff0c;最大为 16384。CACHEMODEL&#xff1a;表示是否在内存中缓存子表的最近数据…

TCP 和 UDP 在哪些场景下会被使用?

传输层是计算机网络体系结构中的关键层次之一&#xff0c;主要负责向两个主机中的进程之间的通信提供服务。传输层在终端用户之间提供透明的数据传输&#xff0c;向上层提供可靠的数据传输服务。它在给定的链路上通过流量控制、分段/重组和差错控制来保证数据传输的可靠性。传输…

基于秒杀-----分布式锁----lua脚本

基于商品显示秒杀-一人一单业务_xzm_的博客-CSDN博客改进 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁 分布式锁的五个基本要求&#xff1a;多进程可见&#xff0c;互斥&#xff0c;高可用&#xff0c;高性能&#xff0c;安全性 三种实现方式 redi…

Java jvm调优

系列文章目录 文章目录 系列文章目录前言JVM 基础面试题11. JDK&#xff0c;JRE以及JVM的关系2. 我们的编译器到底干了什么事&#xff1f;3. 类加载机制是什么&#xff1f;3.1 装载(Load)3.2 链接(Link)验证(Verify)准备(Prepare)解析(Resolve) 3.3 初始化(Initialize) 4. 类加…

SpringBoot框架面试专题(初级-中级)-第二节

欢迎大家一起探讨相关问题&#xff0c;我们共同进步&#xff0c;喜欢的话可以关注点赞&#xff0c;后续会持续更新&#xff0c;谢谢&#xff5e; 问题&#xff1a; 1.如何在Spring Boot中实现事务管理&#xff1f;常用的事务传播机制有哪些&#xff1f; 解析&#xff1a; 在…

人脸识别3:C/C++ InsightFace实现人脸识别Face Recognition(含源码)

人脸识别3&#xff1a;C/C InsightFace实现人脸识别Face Recognition(含源码) 目录 1. 前言 2. 项目安装 &#xff08;1&#xff09;项目结构 &#xff08;2&#xff09;配置开发环境(OpenCVOpenCLbase-utilsTNN) &#xff08;3&#xff09;部署TNN模型 &#xff08;4&a…

C++类型转换运算符(dynamic_cast, const_cast, static_cast)

C类型转换运算符 1. dynamic_cast2. const_cast3. static_cast4. reinterpret_cast 摘自以下图书: 《C Primer Plus》Stephen Prata 在 C的创始人 Bjame Stroustrup 看来&#xff0c;C 语言中的类型转换运算符太过松散。例如&#xff0c;请看下面的代码&#xff1a; struct …