Java虚拟机必知的四大知识要点

作者 : 开心源码 本文共3833个字,预计阅读时间需要10分钟 发布时间: 2022-05-12 共138人阅读

作者 | 郑雨迪
来源 | 极客时间《深入拆解 Java 虚拟机》

作为一位 Java 程序员,在尽情享受 Java 虚拟机带来好处的同时,我们还应该去理解和思考“这些技术特性是如何实现的”,去理解最底层的原理。只有熟习 JVM,你才能在遇到 OutOfMemory 等异常时,不会束手无策,不会一脸懵逼地上网找处理办法,最后就算改了几个启动参数处理了问题,也还是云里雾里。

这次,我会从我专栏里提取了学习 Java 虚拟机的 X 大知识要点,助力大家深入了解 JVM,知其然也知其所以然。 不过你在看知识点之前,最好能问问自己你会怎样答复,再和我提供的内容做比照,这样子提升会比较显著。

第一大知识要点:Java 字节码是如何在虚拟机里运行的?

我将以 HotSpot 虚拟机为例,从虚拟机以及底层硬件两个角度,来分享解析。

1、从虚拟机视角来看

执行 Java 代码首先需要将它编译而成的 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被存放于方法区中。实际运行时,虚拟机会执行方法区内的代码。

假如你熟习 X86 的话,你会发现这和段式内存管理中的代码段相似。而且,Java 虚拟机同样也在内存中划分出堆和栈来存储运行时数据。不同的是,Java 虚拟机会将栈细分为面向 Java 方法的 Java 方法栈,面向用 C++ 写的 native 方法的本地方法栈,以及存放各个线程执行位置的 PC 寄存器。

在运行过程中,每当调用进入一个 Java 方法,Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。

当退出当前执行的方法时,不论是正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。

2、从硬件视角来看

Java 字节码无法直接执行。因而,Java 虚拟机需要将字节码翻译成机器码。

在 HotSpot 里面,上述翻译过程有两种形式:第一种是解释执行,相当于同声传译,即每解析一条字节码,便翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),则相当于线下翻译,即将整个方法中所包含的字节码统一翻译成机器码后在执行。

前者的优势在于无需等待编译,然后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合理解释执行和即时编译两者的优点。它会先解释执行字节码,然后将其中反复执行的热点代码,以方法为单位进行即时编译。

第二大知识要点:Java 虚拟机是如何加载 Java 类的?

Java 虚拟机加载 Java 类的过程可分为加载、链接以及初始化三大步骤。

加载是指查找字节流,并且据此创立类的过程。加载需要借助类加载器,在 Java 虚拟机中,类加载器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。

链接,是指将创立成的类合并至 Java 虚拟机中,使之能够执行的过程。链接还分验证、准备和解析三个阶段,分别完成“验证被加载类能否满足 Java 虚拟机束缚”,“为被加载类静态字段分配内存”,以及“将被加载类中的符号引用解析成为实际引用”的工作。其中,Java 虚拟机规范并不要求解析阶段肯定要在链接步骤中完成。

初始化,则是为标记为常量值的字段赋值,以及执行 <clinit> 方法的过程。类的初始化仅会被执行一次,这个特性被用来实现单例的推迟初始化。

第三大知识要点:Java 虚拟机是如何进行垃圾回收的?

Java 虚拟机中的垃圾回收器采用可达性分析来探究所有存活的对象。它从一系列 GC Roots 出发,边标记边探究所有被引用的对象。为了防止在标记过程中堆栈的状态发生改变,Java 虚拟机采取安全点机制来实现 Stop-The-World 操作,暂停其余非垃圾回收线程。

回收垃圾对象的内存共有三种基础算法,分别为:会造成内存碎片的清理算法、性能开销较大的压缩算法、以及堆使用效率较低的复制算法。

通常来说,Java 虚拟机会采用分代回收的思想,将堆划分为新生代和老年代,并且通过在不同代中应用不同的垃圾回收算法。

传统的做法是将新生代再划分为 Eden 区和两个大小一致的 Survivor 区。在只针对新生代的 Minor GC 中,Eden 区和非空 Survivor 区的存活对象会被复制到空的 Survivor 区中,当 Survivor 区中的存活对象复制次数超过肯定数值时,它将被晋升至老年代。

由于 Minor GC 只针对新生代进行垃圾回收,所以在枚举 GC Roots 的时候,它需要考虑从老年代到新生代的引用。为了避免扫描整个老年代,Java 虚拟机引入了名为卡表的技术,大致地标出可能存在老年代到新生代的引用的内存区域。

G1 垃圾回收器将堆划分为多个等大的区域,每个区域都可以充任 Eden 区,Survivor 区或者者老年代区。G1 会优先收集垃圾最多的区域,从而最大化垃圾回收的效益。这也是 Garbage First 名字的由来。

Java 11 中引入的试验性垃圾回收器 ZGC,仅在扫描 GC Roots 时请求 Stop-The-World,暂停应用线程。因而,它声称可将 GC 暂停时间控制在 10ms 以下。ZGC 暂时没有应用分代回收的思路,将整个堆空间看成一块,其代价是垃圾回收 CPU 消耗较高。

第四大知识要点:Java 内存模型是什么?

在现代计算机系统中,代码通常不会按照书写顺序执行。造成这一情况的起因有三个,分别为编译器的重排序,解决器的乱序执行,以及内存系统的重排序。

以内存系统重排序为例,在多解决器体系架构下,每个解决器都可能缓存了一部分数据。因为时刻保持缓存数据与内存数据同步的性能代价太大,因而部分体系架构可能允许缓存数据与内存数据不同步。这对 Java 程序的影响便是,两个不同的 Java 线程在同一时间内看到的同一块内存地址中的值可能不同。

Java 内存模型是针对上述问题而提出的一套规范,用以允许 Java 程序员更为细致地定义 Java 程序的内存行为。它通过定义了一系列的 happens-before 操作,让应用程序开发者能够轻易地表达不同线程的操作之间的内存可见性。

在遵守 Java 内存模型的前提下,即时编译器以及底层体系架构能够调整内存访问操作,以达到性能优化的效果。假如开发者没有正确地利用 happens-before 规则,那么将可能导致数据竞争。

Java 内存模型是通过内存屏障来禁止重排序的。对于即时编译器来说,内存屏障将限制它所能做的重排序优化。对于解决器来说,内存屏障会导致缓存的刷新操作。

扩展阅读

我的专栏《深入拆解 Java 虚拟机》已完结,非常感谢在我专栏完结之前的 16000 多名订阅客户,在未理解完整内容的情况下,毅然订阅了我的专栏。为不辜负大家的信任,我几乎每篇专栏都会大量阅读 HotSpot 的源代码,和同事探讨实现背后的设计理念,在这个过程中,我也发现了少量 HotSpot 中的 Bug,或者年久失修的代码,又或者者是设计不正当的地方,这大概算作写专栏和我本职工作重叠的地方吧。

专栏尽管到此已经结束了,但是并不代表你对 Java 虚拟机学习的中止。我想,专栏的内容仅仅是为你打开了 JVM 学习的大门,里面的风景,还是需要你自己来探究。在文章的后面,我列出了一系列的 Java 虚拟机技术的相关博客和阅读资料,你依然可以继续加餐。

你可以关注国内几位 Java 虚拟机大咖的知乎或者微信公众号:

  • R 大,个人认为是中文圈子里最理解 Java 虚拟机设计实现的人,你可以关注他的知乎账号:

    https://www.zhihu.com/people/rednaxelafx

  • 你假笨,原阿里 Java 虚拟机团队成员,现 PerfMa CEO,公众号:你假笨

  • 江南白衣,唯品会资深架构师,公众号:春天的旁边;

  • 占小狼,美团基础架构部技术专家,公众号:占小狼的博客

  • 杨晓峰,前甲骨文首席工程师,公众号:小肥羊聊 Java。

假如英文阅读没问题的话,你可以关注:

  • Cliff Click

    http://cliffc.org/blog/

  • Aleksey Shipil?v

    https://shipilev.net/

  • Nitsan Wakart

    http://psy-lob-saw.blogspot.com/

你也可以关注

  • Java Virtual Machine Language Submit

    http://openjdk.java.net/projects/mlvm/jvmlangsummit/

  • Oracle Code One

    https://www.oracle.com/code-one/index.html

关于 Java 虚拟机的演讲,以便掌握 Java 的最新发展动向。

当然,假如对 GraalVM 感兴趣的话,你可以订阅我们团队的博客,之后我会考虑逐一进行翻译

https://medium.com/graalvm

至于其余阅读材料,你可以参考 R 大的这份书单

https://www.douban.com/doulist/2545443/

或者者这个汇总贴

deephacks/awesome-jvm

假如本专栏已经激发了你对 Java 虚拟机的学习热情,那么我建议你着手阅读 HotSpot 源代码,并且回馈 OpenJDK 开源社区。这种回馈并不肯定是提交 patch,也可以是 bug report 或者者改进建议等等。

道阻且长,努力加餐~!

可以说,Java 虚拟机就是每一位 Java 工程师进阶加薪的利器,你想往上升,你想深入技术,不想一直停留在简单开发,或者者你在做 Java 性能分析、调优工作时,那么,Java 虚拟机绝对是一把助力的利剑。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java虚拟机必知的四大知识要点

发表回复