深入理解Java虚拟机之JVM内存布局篇

Heap 堆区

=======

​ Heap堆区是Java发生OOM(Out Of Memory)故障的地方,堆中存储着我们平时创建的实例对象,最终这些不再使用的对象会被垃圾收集器回收掉,而且堆是线程共享的。一般情况下,堆所占用的内存空间是JVM内存区域中最大的,我们在平时编码中,创建对象如果不加以克制,内存空间也会被耗尽。堆的内存空间是可以自定义大小的,同时也支持在运行时动态修改,通过 -Xms 、-Xmx 这两参数去改变堆的初始值和最大值。-X指的是JVM运行参数,ms 是memory start的简称,代表的是最小堆容量,mx是memory max的简称,代表的是最大堆容量;如 -Xms256M代表堆的初始值是256M,-Xmx1024M代表堆的最大值是1024M。由于堆的内存空间是可以动态调整的,所以在服务器运行的时候,请求流量的不确定性可能会导致我们堆的内存空间不断调整,会增加服务器的压力,所以我们一般都会将JVM的Xms和Xmx的值设置成一样,同样也为了避免在GC(垃圾回收)之后调整堆大小时带来的额外压力。

​ 堆区分为两大区:Young区和Old区,又称新生代和老年代。对象刚创建的时候,会被创建在新生代,到一定阶段之后会移送至老年代,如果创建了一个新生代无法容纳的新对象,那么这个新对象也可以创建到老年代。如上图所示。新生代分为1个Eden区和2个S区,S代表Survivor。大部分的对象会在Eden区中生成,当Eden区没有足够的空间容纳新对象时,会触发Young Garbage Collection,即YGC。在Eden区进行垃圾清除时,它的策略是会把没有引用的对象直接给回收掉,还有引用的对象会被移送到Survivor区。Survivor区有S0和S1两个内存空间,每次进行YGC的时候,会将存活的对象复制到未使用的那块内存空间,然后将当前正在使用的空间完全清除掉,再交换两个空间的使用状况。如果YGC要移送的对象Survivor区无法容纳,那么就会将该对象直接移交给老年代。上面说了,到一定阶段的对象会移送到老年区,这是什么意思呢?每一个对象都有一个计数器,当每次进行YGC的时候,都会 +1。通过-XX:MAXTenuringThrehold参数可以配置当计数器的值到达某个阈值时,对象就会从新生代移送至老年代。该参数的默认值为15,也就是说对象在Survivor区中的S0和S1内存空间交换的次数累加到15次之后,就会移送至老年代。如果参数配置为1,那么创建的对象就会直接移送至老年代。具体的对象分配即回收流程可观看下图所示。

深入理解Java虚拟机之JVM内存布局篇

如果Survivor区无法放下,或者创建了一个超大新对象,Eden和Old区都无法存放,就会触发Full Garbage Collection,即FGG,便再尝试放在Old区,如果还是容纳不了,就会抛出OOM异常。在不同的JVM实现及不同的回收机制中,堆内存的划分方式是不一样的。

Metaspace 元空间

=============

​ 在JDK8版本中,元空间的前身Pern区已经被淘汰。在JDK7及之前的版本中,Hotspot还有Pern区,翻译为永久代,在启动时就已经确定了大小,难以进行调优,并且只有FGC时会移动类元信息。不同于之前版本的Pern(永久代),JDK8的元空间已经在本地内存中进行分配,并且,Pern区中的所有内容中字符串常量移至堆内存,其他内容也包括了类元信息、字段、静态属性、方法、常量等等都移至元空间内。

JVM Stacks 虚拟机栈

===============

​ 栈(Stack)是一个先进后出的数据结构,先进后出怎么理解?类似于我们平时打羽毛球时,装羽毛球的球筒,第一个先放进去的往往最后一个才能拿出来,最后放进去的一个最先拿出来。

​ 相对于基于寄存器的运行环境来说,JVM是基于栈结构的运行环境。因为栈结构移植性更好,可控性更强。JVM的虚拟机栈是描述Java方法执行的内存区域,并且是线程私有的。栈中的元素用于支持虚拟机进行方法调用,每个方法从开始调用到执行完成的过程,就是栈帧从入帧到出帧的过程。在活动线程中,只有位于栈顶的帧才是有效的,称为当前栈帧。正在执行的方法称为当前方法,栈帧是方法运行的基本结构。在执行引擎运行时,所有指令都只能针对当前栈帧进行操作。而StackOverflowError表示请求的栈溢出,导致内存耗尽,通常出现在递归方法中。如果把JVM当做一个棋盘,虚拟机栈就是棋盘上的将/帅,当前方法的栈帧就是棋子能走的区域,而操作栈就是每一个棋子。操作栈的压栈和出栈如下图所示:

深入理解Java虚拟机之JVM内存布局篇

​ 虚拟机栈通过压栈和出栈的方式,对每个方法对应的活动栈帧进行运算处理,方法正常执行结束,肯定会跳转到另外一个栈帧上。在执行的过程中,如果出现异常,会进行异常回溯,返回地址通过异常处理表确定。栈帧在整个JVM体系中的地位颇高,包括局部变量表、操作栈、动态连接、方法返回地址等。

​ 下面对栈帧的各个活动栈帧进行简要的分析

​ (1)局部变量表

​ 局部变量表是存放方法参数和局部变量的区域。我们都知道,类属性变量一共要经历两个阶段,分为准备阶段和初始化阶段,而局部变量是没有准备阶段,只有初始化阶段,而且必须是显示的。如果是非静态方法,则在index[0]位置上存储的是方法所属对象的实例引用,随后存储的是参数和局部变量。字节码指令中的STORE指令就是将操作栈中计算完成的局部变量写回局部变量表的存储空间内。

​ (2)操作栈

​ 操作栈是一个初始状态为空的桶式结构栈。在方法执行过程中,会有各种指令往栈中写入和提取信息。JVM的执行引擎是基于栈的执行引擎,其中的栈指的就是操作栈。字节码指令集的定义都是基于栈类型的,栈的深度在方法元信息的stack属性中,下面就通过一个例子来说明下操作栈与局部变量表的交互:

public int add() {

int x = 10;

int y = 20;

int z = x + y;

return z;

}

字节码操作顺序如下:

public int add();

Code:

0: bipush 10 // 常量 10 压入操作栈

2: istore_1 // 并保存到局部变量表的 slot_1 中 (第 1 处)

3: bipush 20 // 常量 20 压入操作栈

5: istore_2 // 并保存到局部变量表的 slot_2 中

6: iload_1 // 把局部变量表的 slot_1 元素(int x)压入操作栈

7: iload_2 // 把局部变量表的 slot_2 元素(int y)压入操作栈

8: iadd // 把上方的两个数都取出来,在 CPU 里加一下,并压回操作栈的栈顶

9: istore_3 // 把栈顶的结果存储到局部变量表的 slot_3 中

10: iload_3

11: ireturn // 返回栈顶元素值

​ 第 1 处说明:局部变量表就像一个快递柜,有着很多的柜子,依次编号为1,2,3,…,n,字节码指令 istore_1 就代表打开了 1 号柜子,再把栈顶中的值 10 存进去。栈就好如一个桶,任何时候只能对桶口的元素进行操作,所以数据只能在栈顶进行存取。部分指令可以直接在柜子里面直接进行,比如 iinc指令,直接对抽屉里的数值进行 +1操作。我们经常遇到的 i++ 和 ++i,通过字节码对比起来,答案一下子就一目了然了。如下表格所示:

深入理解Java虚拟机之JVM内存布局篇

​ 左列中,iload_1 从局部变量表的第1号柜子取出一个数,压入栈顶,下一步直接在柜子里实现 + 1的操作,而这个操作时对栈顶元素的值没有任何影响,所以 istore_2 只是把栈顶元素赋值给 a,而右列,它是先在柜子里面进行 +1的操作,然后再通过 iload_1 把第1号柜子里的数压入栈顶,所以istore_2赋给a的值是 +1 之后的值。扩展下,i++ 并非是原子操作。即使通过volatile关键字来修饰,多线程情况下,还是会出现数据互相覆盖的情况。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

写在最后

可能有人会问我为什么愿意去花时间帮助大家实现求职梦想,因为我一直坚信时间是可以复制的。我牺牲了自己的大概十个小时写了这片文章,换来的是成千上万的求职者节约几天甚至几周时间浪费在无用的资源上。

复习一周,字节跳动三场技术面+HR面,不小心拿了offer

复习一周,字节跳动三场技术面+HR面,不小心拿了offer

上面的这些(算法与数据结构)+(Java多线程学习手册)+(计算机网络顶级教程)等学习资源
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

上面的这些(算法与数据结构)+(Java多线程学习手册)+(计算机网络顶级教程)等学习资源
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值