JVM底层实现原理(字节码执行细节与垃圾回收)

1、何为JVM:
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
2、JVM的重要性
在学习java时我们能会知道java语言为什么会如此受人们爱戴,一部分原因是来自它的跨平台性,之所以跨平台是因为java笼统来说属于编译型语言(当然细致来说是:编译——解释型),编译一次后以字节码文件(.class)文件保留在机器中,JVM在解析字节码文件时,可以根据操作系统的不同而解析成相应的机器码(隐藏底层细节的特性)。Java源文件执行过程如下:
在这里插入图片描述

3、JVM、JRE、JDK
说到JVM必不可少的会联系起JRE、JDK。搞清三者之间的关系是每位java学者都必须掌握的。
JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。所有的Java 程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序,安装JRE即可。

JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是 安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。

JVM(JavaVirtualMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。

以上定义太过于官方言语,简记:某某某,是。。。,提供。。。,具体如下
JDK:Java开发工具包,是Java开发环境的核心组件,提供编译、调试和运行一个Java程序所需要的所有工具,可执行文件和二进制文件。
JRE:Java运行时环境,是JVM的实现,提供了运行Java程序的平台。
JVM:Java虚拟机,是负责将字节码转换为机器码的地方,提供了内存管理/垃圾回收和安全机制等。

图解:
在这里插入图片描述
4、JVM的底层原理
在这里插入图片描述
(1)、总体组成分为三部分:
类装载器(ClassLoader):用来装载.class文件

执行引擎:执行字节码,或者执行本地方法

运行时数据区(内存模型):方法区(元空间)、堆内存、虚拟机栈(线程)、PC寄存器(程序计数器)、本地方法栈

常考重点:运行时数据区各个区的作用
内存堆:存放new出来的对象。
虚拟机栈:局部变量(方法中的变量)、引用变量。
方法区:类信息(对象的代码、属性、方法)、常量、静态变量。
本地方法栈:提供本地方法的服务。
程序计数器:字节码的位置指示器。
其中方法区和内存堆是线程共享的。
(2)、工作原理
假设一java程序如下:
在这里插入图片描述
main方法是程序的主入口,率先会在“栈”中创建一个main线程,它具有自己独立的内存空间,相当于一个小的“栈”,同时它会给main方法其它对象分配内存空间,称为:“栈帧”。如图:
在这里插入图片描述
思考:compute()方法中有自己的变量和表达式,JVM该如何去执行它们呢?
compute()栈帧内存中又分为四个部分:局部变量表、操作数栈、动态链接、方法出口。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
顾名思义,局部变量表是给局部变量分配内存空间的地方,操作数栈是给变量赋值时存放数值的地方,此时JVM通过执行上示图中的指令码(出栈入栈)来完成赋值操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
创建一个新的线程时,不仅会在栈中分配一块内存空间还分在程序计数器区中分配一块内存空间,用来记录JVM执行的位置(红色框):
在这里插入图片描述
在这里插入图片描述
程序计数器的作用:当有优先级更高的线程抢占cpu时,该线程就会被挂起,再次执行时cpu可以根据程序计数器记录的位置来承接执行。
那么谁来修改程序计数器的值及其它动作的驱动呢?就是它——字节码执行引擎(谨记组成图)
动态链接:当某个对象调用某个方法时,要在方法区找到它的位置,而动态链接就是记录所在方法区的入口位置。
方法出口:当mian方法调用并执行完compute方法时,找到方法出口记录的位置并继续执行下一条语句。
在这里插入图片描述
当main方法中的math对象要去调用其它方法或变量时,会先去堆中找到内存地址。说白了“堆”就是存放对象内存地址空间的。
5、JVM垃圾回收原理
垃圾回收原理时进行JVM调优的重要路径也是JVM原理的重要知识点。回收方法(算法)有:标记-整理算法、标记-清除算法、复制算法分代收集算法
这里具体讲一下JVM常用的“分代收集算法”和“复制算法”。
JVM的内存可以分为堆内存和非堆内存。堆内存分为年轻代和老年代。年轻代又可以进一步划分为一个Eden(伊甸)区和两个Survivor(幸存)区组成。
在这里插入图片描述
(1)、JVM堆内存的分配:
如图:
在这里插入图片描述

JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64。JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。因此我们一般设置-Xms和-Xmx相等以避免在每次GC 后调整堆的大小。

通过参数-Xmn2G 可以设置年轻代大小为2G。通过-XX:SurvivorRatio可以设置年轻代中Eden区与Survivor区的比值,设置为8,则表示年轻代中Eden区与一块Survivor的比例为8:1。注意年轻代中有两块Survivor区域。
(2)、老年代和年轻代:
Minor GC(年轻代GC):
对象优先在Eden中分配,当Eden中没有足够空间时,虚拟机将发生一次Minor GC,因为Java大多数对象都是朝生夕灭,所以Minor GC非常频繁,而且速度也很快。

Full GC(老年代GC):
Full GC是指发生在老年代的GC,当老年代没有足够的空间时即发生Full GC,发生Full GC一般都会有一次Minor GC。
(3)、垃圾回收方式:
大对象和长期存活的对象将进入老年代,每一次MinorGC(年轻代GC),对象年龄就大一岁,默认15岁晋升到老年代,通过-XX:MaxTenuringThreshold设置晋升年龄。
第一次MinorGC:Eden区被GC-Root标记的对象为“非垃圾对象”,就会被“复制算法”复制到Survivor1区,幸存下来的分代年龄就会加1
第二次MinorGC:Eden、Survivor1区幸存下来的对象会共同挪到Survivor2区,同时分代年龄加1
第三次MinoorGC:Eden、Survivor2区幸存下来的对象会共同挪到Survivor2区,同时分代年龄加1
。。。。。以此反复,当对象年龄等于15时,会被放到老年代区
当老年代内存满时,会进行一次full gc,伴随着STW(Stop The Work):清理垃圾时会停掉其他线程。
优化——减少STW——减少full gc:因为full gc是在整个堆里清理垃圾,STW发生概率大。

(4)JVM如何判定一个对象是否应该被回收?(重点掌握)
判断一个对象是否应该被回收,主要是看其是否还有引用。判断对象是否存在引用关系的方法包括引用计数法以及root根搜索方法
root根搜索方法:可达性分析算法————GC Roots
将“GC Roots”对象作为起点,从这些结点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象。
GC Roots根结点的对象:
a、栈引用的对象
b、方法区中类静态属性引用的对象
c、方法区中常量引用的对象
d、native方法引用的对象
在这里插入图片描述

(5)、垃圾回收算法(重点)
HotSpot虚拟机采用了root根搜索方法来进行内存回收,常见的回收算法有标记-清除算法,复制算法和标记整理算法。

标记-清除算法:
标记-清除算法执行分两个阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,并且会产生内存碎片。
在这里插入图片描述
复制算法:
复制算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。复制算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
在这里插入图片描述
标记-整理算法:
标记-整理算法结合了“标记-清除”和“复制”两个算法的优点。也是分两个阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
在这里插入图片描述
垃圾回收算法是垃圾收集器的算法实现基础,年轻代垃圾回收一般采用复制算法(回想到年轻代的多区结构:Eden,s1,s2),老年代垃圾回收一般采用标记-清除和标记-整理算法。

(6)垃圾收集器(主要掌握CMS)
结合第五知识点,垃圾收集器就是垃圾回收算法的具体实现。
主要包括七种,即Serial、Serial Old、ParNew、Darallel Scavenge、Parallel Old以及CMS、G1收集器。如下图所示:
在这里插入图片描述

CMS的垃圾收集过程分为4步:
初始标记:需要“Stop the World”,初始标记仅仅只是标记一下GC Root能直接关联到的对象,速度很快。
并发标记:是主要标记过程,这个标记过程是和用户线程并发执行的。
重新标记:需要“Stop the World”,为了修正并发标志期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录(停顿时间比初始标记长,但比并发标记短得多)。
并发清除:和用户线程并发执行的,基于标记结果来清理对象。
在这里插入图片描述
那么问题来了,如果在重新标记之前刚好发生了一次MinorGC,会不会导致重新标记阶段Stop the World时间太长?
答:不会的,在并发标记阶段其实还包括了一次并发的预清理阶段,虚拟机会主动等待年轻代发生垃圾回收,这样可以将重新标记对象引用关系的步骤放在并发标记阶段,有效降低重新标记阶段Stop The World的时间。
CMS垃圾回收器的优缺点分析
CMS以降低垃圾回收的停顿时间为目的,很明显其具有并发收集,停顿时间低的优点。
缺点主要包括如下:
在这里插入图片描述

*参考视频:https://ke.qq.com/webcourse/index.html#cid=230866&term_id=100272363&taid=3625450614261202&vid=5285890807081339152*
参考资料:牛客网《Java开发岗高频面试题全解析》

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Senye_ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值