深入理解JVM内存模型

JVM 内存模型

运行时数据区域

程序计数器(Program Conunter Regisiter)

程序计数器是一个比较小的内存空间,可以看作是当前线程执行的字节码行号指示器。本质就是记录字节码执行顺序。 在《Java 虚拟机规范》中没有任何 OutOfMemoryError 情况的区域。

虚拟机栈(JVM Stack)

虚拟机栈,存放的是线程运行时内部的局部变量,也可以理解为线程栈。

每个方法被执行的时候, 虚拟机会创建一个栈帧(Stack Frame)用于存放局部变量表(local variable),操作数栈(operand stack),动态连接,方法出口等信息。

栈帧(Stack Frame)随着方法的调用而创建,随着方法的结束而销毁(不论是正常结束还抛出异常)。

字节码指令分析(描述 JVM Stack 操作过程)

publicintadd(){inta =1;intb =2;intc = b - a;returnc;}0iconst_1//将 a 压入局部变量表栈顶1istore_1//对 a 进行赋值 1  2iconst_2//将 b 压入局部变量表栈顶3istore_2//对 b 进行赋值 24iload_2//读取 b 到操作数栈5iload_1//读取 a 到操作数栈6isub//执行 b - a7istore_3//将 int 类型的值存入局部变量表 3 8iload_3//读取 c 到操作数栈9ireturn//返回复制代码

局部变量表(local variable)

局部变量表存放了各种编译期 Java 虚拟机基本数据库类型(boolean、byte、char、short、int、float、long、dubble)和对象引用(reference 类型,即对象的起始位置指针或者对象句柄), 对象的真实数据通常存放在堆空间。

局部变量表中的存储空间通过变量槽(slot) 来表示,其中 64 位长度的 long  和 double 占 2 个变量槽。

操作数栈(operand stack)

每个栈帧都包含一个操作数栈的先进先出(FIFO)栈,栈帧中操作数栈的深度由编译期决定,并且通过方法的 code 属性保存以及提供给栈帧使用。

动态链接

每个栈帧都包含一个指向当前方法所在类型的运行时常量池的引用。以便对当前方法的代码实现动态链接

在 class 文件中,一个方法如果要调用其它方法, 或者访问局部成员变量,则需要将符号引用(synbolic reference)来表示,动态链接的作用就是将这些符号引用转换为对实际方法的直接引用

方法出口

方法正常完成,当前栈帧恢复调用者的责任,包括恢复调用者的局部变量表,操作数栈,以及正确的程序计数器递增。跳过刚才执行的方法调用指令等,低哦啊用着的代码被调用的方法正返回值压入调用者操作数栈后,会继续正常执行。

方法一场完成,某些指令导致了 JVM 虚拟机抛出异常,或者用户显示的通过thorw关键字跑出一场,同时在改该方法中没有捕获异常。如果方法异常调用完成,那不一定有方法返回值返回给调用者。

本地方法栈(Native Method Stack)

为本地方法所分配的内存空间,就是为native关键字修饰的方法提供服务的。

本地方法主要是 Java 来调用 C/C++ 函数库的调用方法。

方法区(Method Area)

主要存放数据有:常量,静态变量,类信息。

方法区存放的是静态变量的内存地址, 方法区里面有一个元空间, 在JDK1.8 之前叫永久代。

堆(Heap)

JVM 管理的最大的一块内存空间。与堆相关的一个重要概念是垃圾收集器。几乎所有的垃圾收集器都是采用分代收集算法,所以对内存空间也是基于这一点进行相应的划分:新生代和老年代,新生代分为Eden 空间、From Survivor 空间、To Survivor 空间。

对象创建的过程中首先会存在 Eden 区,然后经过 minor gc 过后进入 survivor ,进过 15 次 survivor 转移过后,进入老年代。

如果内存都不够用了就触发 full gc, 再次触发 GC 过后无法分配申请内存,JVM 就会抛出 OOM。

分析对象是否存在引用,是否被回收采用的是 GC ROOT 可达性分析。

直接内存(Direact Memory)

直接内存,不是 JVM 来管理,是通过操作系统来管理的, 与 Java NIO 密切相关。 Java 通过DirectByteBuffer来操作直接内存。

JVM 内存参数设置

内存参数配置

Spring-Boot 程序的 JVM 内存参数设置格式(Tomcat 启动直接在 bin 目录下的 Catalina.sh 文件设置)

java -Xms2048m -Xmx2048m  -Xmn1024 -Xss512k -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -jar  xxx-xxx.jar复制代码

关于元空间JVM 有两个:-XX:MetaspaceSize=N 和 -XX:MaxMetaspaceSize=N,对于 64 位 JVM 来说, 元空间默认是 21MB,默认的元空间的最大值是无限

-XX:MaxMetaspaceSize: 设置元空间最大值,默认是 -1, 即不限制,或者说是受限制于本地内存大小。

-XX:MetaspaceSize:指定元空间的初始大小,以字节为单位,默认是 21M,达到该值过后就会触发 full gc 进行类型卸载,同时收集器会对该值进行调整;如果释放了大量的空间就适当降低该值;如果释放了很少的空间,那么就在不超过 -XX:MaxMetaspaceSize (如果设置)的情况下,适当提高该值。

由于调整元空间大小需要 full gc , 这是一个非常昂贵的操作,如果在启动过程中发生大量 full gc, 通常都是由于永久代或者元空间发生了大小调整,基于这种情况,一般建议在 JVM 参数将 MaxMetaspaceSize 和 MetaspaceSize 设置成一样的值,并设置得比初始值要大,对于 8G 的物理内存来说我们通常都会将这两个值设置为 256M。

堆空间内存溢出

importjava.util.ArrayList;importjava.util.List;publicclassHeapOverFlowTest{byte[] a =newbyte[1024*1024*2];// 2mbpublicstaticvoidmain(String[] args){        List list =newArrayList<>();while(true) {            list.add(newHeapOverFlowTest());        }    }}// 输出结果Exception in thread"main"java.lang.OutOfMemoryError: Java heap spaceat cn.edu.cqvie.jvm.HeapOverFlowTest.(HeapOverFlowTest.java:8)at cn.edu.cqvie.jvm.HeapOverFlowTest.main(HeapOverFlowTest.java:13)复制代码

虚拟机栈内存溢出

publicclassStackOverFlowTest{// JVM 设置// -Xss128k, -Xss默认1Mstaticintcount =0;staticvoidredo(){        count++;        redo();    }publicstaticvoidmain(String[] args){try{            redo();            System.out.println(count);        }catch(Throwable t) {            t.printStackTrace();        }    }}// 输出结果: 栈溢出java.lang.StackOverflowErrorat cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)....复制代码

总结:

-Xss 设置越小 count 值越小,说明一个线程栈里能够分配的栈帧就越小,但是对于 JVM 整体来说能够开启的线程数就会更多。

方法区内存溢出

需要注意的是 1.8 内模型中,将运行时常量池数据放入堆中,所以我们限制方法区的大小对运行时常量池的限制毫无意义。最终也只会抛出java.lang.OutOfMemoryError: Java heap space异常。

下面通过GCLib 模拟方法区溢出模拟的一个例子。

/**

* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

*/publicclassMyTest4{publicstaticvoidmain(String[] args){for(; ; ) {            Enhancer enhancer =newEnhancer();            enhancer.setSuperclass(MyTest4.class);            enhancer.setUseCache(false);            enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) ->                    proxy.invoke(obj, args1));            System.out.println("hello world");            enhancer.create();        }    }}//输出结果Caused by: java.lang.OutOfMemoryError: Metaspaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:756)  ......复制代码

JVM 监控工具

VisualVM

VisualVM 提供在 Java 虚拟机 (Java Virutal Machine, JVM) 上运行的 Java 应用程序的详细信息。在 VisualVM 的图形用户界面中,可以方便、快捷地查看多个 Java 应用程序的相关信息。

作者:大漠北

链接:https://juejin.cn/post/6925664904833662983

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容