性能文章>带你用Java实现JVM篇一:实现字节码文件解析器>

带你用Java实现JVM篇一:实现字节码文件解析器原创

245528

大家好,我就是明明可以靠脸吃饭,却偏偏抢大家饭碗的硬核男人子牙老师。

 

今天开始更新正式内容的第一篇:带你用Java实现字节码文件解析器。什么是解析器?解析器干些什么?要怎么实现?有哪些需要注意的地方?Hotspot源码中有解析器吗?是哪个……是不是突然脑海中就蹦出来了这么多问题?么事,本文都会讲到。看完写不出来怎么办?我给你选个楼层高的,你来跳,保证绝对是人生销魂一跳!

 

这套教程的定位是只要会Java,对JVM底层原理、对Hotspot源码不了解的小姥,看了这套专栏课程,也能写出JVM,所以有些偏基础的知识点也会讲。所以有些知识点,如果你对JVM底层原理、对Hotspot源码比较了解,可以跳过,只看你需要的。其实看看也无妨,耽误不了多少时间,就当是复习了。

 

上篇文章说了,Java写JVM,能做的事情有限。如果你想进阶,想实现完整版的JVM,可以自己按照我给的路线折腾,也可以考虑跟着我学习。

 

瓦特?你不会Java?给你一天时间,保证学会。

 

瓦特?你想补JVM底层原理?不想看照着周志明的书讲的,想看讲得独到的,最好是结合Hotspot源码讲的?必须自荐一波我的JVM底层原理

 

 

 

课程目标

 

完成下面代码的解析

public class HelloWorld { 
    public static void main(String[] args) {
        System.out.println("hello, world"); 
    }
}
 

javap命令大家应该用过吧。没用过?看图。小姥们可以在自己机器上用下看看

 

 

javap命令的输出结果是哪来的呢?是将字节码文件按照《字节码文件规范》进行解析出来的,这也是我们今天要实现的字节码解析器要干的事情。《字节码文件规范》哪里看?推荐书《Java虚拟机规范》。如果你不想找,关注公众号【硬核子牙】回复【Hotspot书】获取。在第四章。

 

实现字节码解析器, 跟我们手写JVM有啥关系呢?当然有关系。你可以这样理解,字节码文件是编译系统,即javac命令的输出。是运行系统,即Java命令的输入。两个系统进行对接,编译系统的输出与运行系统的输入不完全匹配,需要一个模块来进行匹配,这个模块就是我们今天要实现的字节码解析器。足见字节码解析器对于JVM的意义。

 

为什么要有一个这样的模块?解耦合呀!这样做,不论你的整个编译系统怎么变化,我修改我的字节码解析器即可完成匹配。这就是大佬们的智慧呀!怎么样?是不是迫不及待地想动手写了。我还不是大佬,但是做大佬做过的事情,我也会成为大佬的。这样的思维没毛病!

 

看下我们要实现的字节码解析器输出结果

 

 

玩转字节码的意义

 

JVM领域的黑科技,基本都是直接玩字节码。

 

如果你之前研究过JDK动态代理底层实现,或者你玩过字节码增强……我推测你以前不是玩,是被玩。你学完今天的内容,你把字节码解析器写出来,你再去研究这些,你会发现,非常痛快。

 

很多人总是想着先把应用层玩透再来学底层,我一向给的答案就是:应用层的知识,工作上够用了就不要再花时间抠细节了,没意义。你就算把spring代码背下来,大佬给你打的Tag还是应用层高手。而且你不会底层,如何玩转应用层。相反,你有底层底子,可以更深度更细节地理解应用层技术。

 

比如你深入理解了字节码文件,你能写出解析器,你再回头看字节码框架Javaassist,一看就会用,甚至深入理解它的底层实现。工作N年后,拼的是学习的速度,拼的是做事的思维……不是谁努力谁勤奋谁学历好谁会拍马屁。拍马屁带有领导属性。生而为人,何必当舔狗,堂堂正正靠实力做人不好吗?

 

前置知识

 

前置知识自然是对字节码文件有个大概的了解。这块我之前讲的JVM底层原理第3节讲得非常详细,我是模拟JVM的字节码解析器,带着大家一个字节一个字节解析的。大家直接看视频吧。可能有点枯燥,建议学习方式是自己先打开一个字节码文件,对照着《字节码文件规范》先自己解析,先有个大概的了解,带着问题来看视频。

 

 

实现细节

 

字节码解析器做的事情就是通过类的全限定名找到硬盘上的字节码文件,载入内存,按照《字节码文件规范》对字节码文件一个字节一个字节的进行解析,将拿到的信息填充进klass模型中。klass模型是什么?你可以理解成就是一个Java类对应的C++对象。klass模型细节可以看我的JVM底层原理视频的第一节。

 

那我们写代码应该怎么写呢?

 

  1. 定义一个类加载器,类加载器中有一个缓存空间,用于存储已经加载并解析过的类信息。加载之前,根据类的全限定名进行判断,如果缓存空间中有,就不加载。否则就触发加载、类解析操作。核心代码如下

 

 
  1. 将硬盘上的字节码文件读入内存,触发解析。核心代码如下

 

 
  1. 解析字节码文件包含:解析常量池、解析接口、解析类字段、解析类方法、解析类属性…细节本文就不展开讲了,大家可以去看我的视频。本文主要关注讲大家实现过程中容易忽略或者不知道标准做法是怎样的细节。

  2. 解析常量池。常量池设计如下,tag用int数组来存储,value用一个map容器来存储

     

这里面比较特殊的是下面这几项,一个常量池项包含三部分内容。

 

 

按照我们的思维,肯定是分三部分进行存储了。那Hotspot是怎么做的呢?看代码

 

 

Hotspot是将除tag外的其他两项数据拼在一起存储的。举个例子

 

 

对于实现解析输出hello world这个程序对应的字节码文件,就常量池这块有点特殊,然后大家在写的过程中可能不知道这个框架怎么搭,基本都讲到了。其他的大家在写的过程中遇到问题就去我提供的代码中找答案吧。实在不了解的可以来问我。

 

建议

 

我会提供我写好的字节码解析器代码,但是我希望大家这样用我的代码:先按照自己的思维自己写,去思考,去犯错,再来我的代码中找答案。不要直接借鉴,更不要直接copy,如果学习,就学出最大效果,不要这样敷衍自己。

 

关注公众号【硬核子牙】回复【手写JVM代码】获取代码。

 

另外,因为Java的语法非常丰富,带来的问题是《字节码规范》非常多,不要照本宣科一次性全撸完,这不是有效的学习方法。你先实现最简单的hello world,其他的规范,随着你写的JVM越来越完善,你都会遇到的,到时再细研究再实现。我也是按照这种方式学习的。做事要讲究节奏。

 

 

专栏目录

 

1、开篇:学这套专栏课程的意义

 

 

结语

 

你好,我是子牙。十余年技术生涯,一路披荆斩棘从小白到技术总监到大厂中间件到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核及特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

 

点赞收藏
分类:标签:
子牙_公号硬核子牙

对编程语言的设计与实现有浓厚兴趣。聚焦Hotspot源码、Linux内核研究,硬核干货分享

请先登录,查看2条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

怒啃 24 小时,终于搞懂上下文切换!

怒啃 24 小时,终于搞懂上下文切换!

【译】一次Docker 中的 Java 内存消耗问题的排查之旅

【译】一次Docker 中的 Java 内存消耗问题的排查之旅

JVM 常见线上问题:CPU100%、内存泄露的问题排查

JVM 常见线上问题:CPU100%、内存泄露的问题排查

FullGC没及时处理,差点造成P0事故

FullGC没及时处理,差点造成P0事故

使用JDK中自带的JVM分析工具解决内存溢出问题

使用JDK中自带的JVM分析工具解决内存溢出问题

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

8
2