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

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

203518

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

 

今天开始更新正式内容的第一篇:带你用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有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

 

分类:
标签:
请先登录,再评论

受益匪浅, 技术的分水岭定位很清晰。应用层重在使用和业务的实现,底层才是根本,从底层的原理知识知道应用层问题的解决。在应用层问题的排查尤为突出,应用层出了问题只要不是使用上的问题,都会涉及网络、内存、CPU等等,往往对底层了解知之甚少最后不了了之,没有结论。要不就得求助于专业技术人员,太被动了。有了底层的功底问题自然迎刃而解,向知识致敬~

2月前

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得