自制JVM需要什么核心技术,JVM原理怎么快速入门

自制一台JVM虚拟机,本质上不是从零复刻HotSpot,而是实现一个能运行特定字节码子集的解释器,新手入门的核心路径,是掌握<关键词:class文件解析>、<关键词:字节码执行引擎>、<关键词:运行时数据区>和<关键词:类加载机制>这四块内容,先跑通“Hello World”,再谈性能。

别被“虚拟机”三个字吓住,说白了,JVM就是一台用软件模拟出来的电脑,它读入.class文件这种“机器码”,在自己的内存里分配对象、执行指令,自制一台迷你版JVM,是理解Java语言本质最快的方式。

【狂神说Java】JVM快速入门篇
加载中
【狂神说Java】JVM快速入门篇

JVM工作原理是什么?先看一个类的完整旅程

搞懂原理之前,先明确一个概念:JVM是规范,不是具体软件,Oracle的HotSpot、Eclipse的OpenJ9,都是这个规范的不同实现,自制JVM,写的是“另一个实现”。

把下面这段代码交给任何一台正常工作的JVM,它内部大致经历了这几个步骤:

public class Demo {
    public static void main(String[] args) {
        String text = "hello";
        System.out.println(text);
    }
}
  • 第一步,装载:Demo.class文件被类加载器找到,解析文件头部的魔数(0xCAFEBABE)确认这是一个合法字节码文件,然后读取常量池、方法表等结构。
  • 第二步,链接与初始化:JVM给Demo类分配好内存空间,把main方法的符号引用替换为直接引用,然后执行静态代码块。
  • 第三步,执行:执行引擎开始逐条读取main方法里的字节码指令,比如ldc指令负责从常量池加载字符串,invokestatic指令负责调用System.out.println方法。
  • 第四步,内存回收:text字符串用完即弃,后台的垃圾回收器(GC)择机清理它占用的堆内存。

这四步,就是一台JVM的核心工作流,自制JVM,本质上就是把这四步按自己的方式重新实现一遍。

如何自制JVM虚拟机?核心技术四件套拆解

这四件套不分先后,但踩坑难度是递增的,建议按顺序逐个击破。

第一关:吃透class文件格式(字节码的“语法书”)

这是整个自制项目的地基。.class文件是有严格二进制布局的,你不能跳过任何一个字节。

  • 魔数与版本号:前4个字节是0xCAFEBABE,后4个字节是编译版本号,版本号不对,JVM直接抛UnsupportedClassVersionError。
  • 常量池(Constant Pool):这是class文件里的“档案馆”,所有类名、方法名、字符串字面量都在这里登记,每个常量项都有自己的tag标签(比如7代表Class引用,9代表Fieldref)。
  • 字段表与方法表:描述类里有哪些成员变量、哪些方法,各自的访问标志(public/private)和描述符是什么。
  • 自制JVM需要什么核心技术,JVM原理怎么快速入门

  • 属性表(Attributes):这是最灵活的区域。Code属性存放方法体真正的字节码指令,StackMapTable属性供类型检查使用。

新手实操建议:不要一开始就手啃[JVM规范文档],直接买一本《深入理解Java虚拟机》读第二章节,或者用一个开源库(如Apache Commons BCEL)帮你解析class文件,你只需要读懂结构,不需要从零写二进制解析。

第二关:运行时数据区怎么分(JVM内存模型图解的简化版)

网上搜“JVM内存模型图解对比”,你会看到一堆堆、栈、元空间的图,自制迷你JVM不用那么复杂,但两块区域是必须的:

  • 堆(Heap):所有new出来的对象都住这里,你的JVM可以用一个简单的ArrayList模拟堆内存,每次创建对象就add进去。
  • 虚拟机栈(VM Stack):每个线程一个栈,每个方法调用对应一个栈帧,栈帧里存着局部变量表、操作数栈和常量池引用。

这里有一个极易混淆的坑:JVM内存模型(JMM)和运行时数据区是两码事,前者讨论多线程下的可见性,后者讨论数据存哪儿,自制JVM初期,你只需要关心后者。

第三关:类加载与双亲委派,要不要自己写?

行业共识认为,自制JVM时,类加载器是最适合“偷懒”的部分,因为你的JVM大概率是跑在标准Java环境里的(用Java语言写的JVM),可以直接复用现有JDK的ClassLoader来帮你读文件。

但如果你想真刀真枪地练手,建议手写一个findClass方法:

  1. 读取.class文件的字节数组。
  2. 调用defineClass方法把字节数组转换成Class对象。
  3. 至于双亲委派模型,新手阶段可以不做,先保证“能加载”就行,“怎么安全地加载”是后面的优化方向。

第四关:字节码执行引擎,这是灵魂所在

执行引擎是JVM最“性感”的部分,你要写一个switch分支,对上200多个操作码(opcode):

  • iconst_0:把整数0压入操作数栈。
  • istore_1:把栈顶的int存到局部变量表第1个槽位。
  • getstatic:获取静态字段的值并压入栈。
  • invokevirtual:调用实例方法。

反复看HotSpot源码分析,不如自己写一遍,网上能找到“Java字节码指令集大全”的表格,翻译成代码并不难,难点在于操作数栈的槽位宽度:long和double类型占两个槽位,处理不当会导致局部变量表错位。

一个务实的建议:先支持int类型和String类型,跑通方法调用、返回值和打印操作,你的JVM已经能运行“Hello World”了,布尔、数组、异常处理,属于进阶补丁。

自制JVM需要什么核心技术,JVM原理怎么快速入门

实战路线:用Java写一个解释器型的JVM

到底用Java语言写还是用C++写?我推荐用Java写,原因很现实:

  • 你能直接用IDE调试,断点打在字节码分发器上,一目了然。
  • Java自带的垃圾回收机制,帮你省去了写内存管理器的地狱工作量,你只需要关注new出来的对象如何在自己的虚拟堆里管理,而不用操心系统级的内存释放。

具体搭建步骤

Step 1:建立工程,定义核心数据结构。

public class MyClass {
    private ConstantPool constants;
    private Method[] methods;
    private Field[] fields;
}

每个Method内部持有CodeAttribute,里面是一串byte[]存放原始字节码。

Step 2:实现字节码解析器。
从ClassFileParser开始,逐字节读入文件头、常量池、方法表,这一步先跑通“解析一个class文件并打印常量池内容”,会获得极强的正反馈。

Step 3:实现最简执行引擎。
核心是一个interpret(MyFrame frame)方法:

while (true) {
    int opcode = code[pc++];
    switch (opcode) {
        case 0x10: // bipush
            int value = code[pc++];
            frame.getOperandStack().push(value);
            break;
        // 其它指令...
    }
}

Step 4:打通“类加载→执行”的闭环。
在main方法里,先调用自己的类加载器加载Test.class,拿到字节码后,创建主线程的栈帧,调用interpret方法执行main方法,当执行到invokestatic指令时,递归创建新栈帧。

遇到Bug怎么办?打印“运行时栈”

这是调试JVM最笨也最有效的方法,在操作数栈的push和pop方法里加上日志输出:

[pc=15] bipush 100 -> 操作数栈: [100, 2, "abc"]

对照Oracle官方发布的[JVM指令集文档],逐条核对,你写出来的不是“眼花缭乱的Bug”,而是“哪条指令的行为偏离了规范”。

自制JVM需要多久?以及新手最容易踩的坑

聊点实际的。自制JVM需要多久? 对于每天能投入2小时的在校生或初级开发者,三到四周可以跑通基本功能;如果是周末时间充裕的上班族,一个半月属于正常周期。

这期间,有四个坑是需要特别留意的:

  • 操作数栈溢出:方法调用嵌套太深(比如递归算斐波那契数列),你的JVM会栈溢出,这是正常的,说明你的栈帧结构写对了,只是没设置栈深度上限。
  • 常量池索引越界:数组越界异常经常不是代码逻辑错了,而是你在解析某个指令时,读多了或读少了字节。排查思路:比对字节码偏移量

    自制JVM需要什么核心技术,JVM原理怎么快速入门

    。

  • 运行缓慢:这个不用太焦虑,行业共识认为,第一版自制JVM,执行效率比HotSpot慢上百倍都是正常的,你没有做JIT编译,相当于用JavaScript解释器去跑Java代码。
  • 误把“简易实现”当“完整JVM”:不用非得支持include动态代理、注解处理器或SPI机制。边界控制力比功能完整度更重要,写一个README,标明“支持Java 8的int运算和字符串拼接”,比做一个什么都跑不动的半成品强得多。

自测清单:你的迷你JVM合格吗?

测试用例目标能力耗时预期
打印 Hello World类加载、main方法解析、字符串常量池第1周达成
定义两个int变量并相加局部变量表、算术指令、操作数栈第2周达成
调用自定义的add(int,int)方法方法栈帧切换、参数传递第2周达成
创建自定义类对象并访问字段对象模型(简单的堆分配)第3周达成
杀死一个自动消失的对象极简标记-清除GC算法第4周可选

高频问题与手写JVM的实战价值

手写一个JVM,对实际工作有什么帮助?

帮助极大但要放平心态,你写完发现,自己并不会因此更懂Spring Boot,但站在JDK源码排查的角度,你比普通业务开发者多了一层底气:当生产环境报出OutOfMemoryError时,你能直观地想象出堆内存里的对象是如何排列的;当类冲突异常(NoClassDefFoundError)出现时,你能立刻意识到这是类加载器的层级问题。

JVM性能调优,和自制JVM有多大关系?

关系有限,自制JVM解决的是“原理问题”,调优解决的是“工程问题”,但有一个明显的隐性收益:为了让自己写的解释器跑得更快,你会去研究公共子表达式消除、逃逸分析的具体行为,这时候回看JVM调优参数-XX:+PrintCompilation,输出结果对你不再是乱码。

想深入学习,应该看HotSpot源码还是看这本书?

先用“写”代替“看”,当你用Java写完第一版解释器后,心里对JVM的行为有了底,再打开OpenJDK 17(近年来的长期支持版本)的HotSpot源码,重点看src/hotspot/share/interpreter目录下的BytecodeInterpreter.cpp,你会惊喜地发现,网络上一幅“JVM工作原理是什么”的示意图,此刻变成了你手里跑过的代码路径,这种共鸣,是任何博客和付费课程都给不了的激励。

自制JVM的本质,是用一段时间的“慢”,换取底层逻辑的“透”,动手写,解决所有焦虑。

首发原创文章,作者:王坚‌,如若转载,请注明出处:https://idctop.com/article/627828.html

赞 (0)
子域名在域名中具体有什么作用,怎么正确设置?
上一篇 2026年9月6日 10:56
服务器配置主要有哪些?,服务器配置怎么选?
下一篇 2026年9月6日 11:01

相关推荐

  • 负载状态与有状态负载到底有何区别?,如何实现

    有状态负载管理的核心在于数据持久化与状态一致性,通过StatefulSet配合分布式存储,可以像管理无状态服务一样优雅地管理有状态应用,有状态负载与无状态负载对比,核心差异在哪?从架构设计的第一天,你就得想清楚应用是有状态还是无状态,无状态是“用完就走”,请求之间彼此独立,扩缩容就像开关灯一样简单,有状态则不同……

    2026年8月2日
    600
  • 备案号可以和域名一起用吗?备案号必须和域名绑定吗?

    备案号不能单独使用,必须和域名绑定在一起才能生效, 备案号是工信部给网站主体发放的许可编号,它本身没有独立意义,只有挂在具体域名下,才代表这个域名可以在国内服务器上正常访问,想绕过绑定关系,把备案号挪到别的域名上,属于违规操作,备案号和域名必须一致吗?先搞清绑定的底层逻辑很多新手站长容易混淆“备案号”和“域名……

    2026年9月1日
    500
  • HTML视频播放代码怎么写?网页嵌入视频代码

    实现HTML视频播放的核心在于使用标准的<video>标签,并配合src属性指定源文件,同时通过controls属性启用浏览器原生控制条,这是目前兼容性最好且无需额外插件的解决方案,在2026年的Web开发环境中,视频加载速度与播放体验依然是影响用户留存的关键因素,许多开发者在面对复杂的视频需求时……

    2026年6月5日
    3000
  • HTML5存储方式有哪些?HTML5本地存储有哪些优缺点

    HTML5提供了LocalStorage、SessionStorage和Web Storage三大主流存储机制,其中LocalStorage适合长期数据持久化,SessionStorage适用于单次会话数据,IndexedDB则专为海量结构化数据设计,在现代Web开发中,数据管理是构建高性能应用的核心环节,过去……

    2026年6月11日
    2910
  • Java常用的服务器都有哪些,哪个最好用?

    Java常用的服务器主要有Apache Tomcat、Eclipse Jetty、Undertow、WildFly(JBoss)、Oracle WebLogic和IBM WebSphere,其中Tomcat凭轻量灵活占据中小型应用的主流,WebLogic与WebSphere则主导金融、电信等大型企业核心系统,选……

    2026年10月7日
    100
  • HTML数据库作品怎么做?前端开发数据库交互实例

    HTML数据库作品并非指将HTML文件直接作为数据库使用,而是指利用前端技术(HTML/CSS/JS)结合后端数据库(如MySQL、MongoDB)或轻量级本地存储方案(如IndexedDB、LocalStorage)所构建的完整数据可视化与交互应用系统,在2026年的Web开发语境下,单纯的前静态页面已无法满……

    服务器宽带 2026年6月6日
    4000
  • access数据库如何查询不重复记录?access查询去重方法

    在Access数据库中查询不重复记录,最核心的方法是使用SQL语句中的SELECT DISTINCT关键字,或者在查询设计视图中勾选“唯一值”属性,这能直接过滤掉重复行,返回去重后的结果,很多初学者在处理Access报表或数据汇总时,经常遇到数据源中存在大量重复项的问题,一个销售记录表中,同一个客户在同一天可能……

    2026年7月3日
    900
  • 未来桥梁服务器有哪些类型,发展趋势如何?

    未来桥梁服务器将走向专用一体机、边缘智能节点与云端算力池三种形态并存的格局,其中具备AI推理能力的边缘计算节点将成为新建桥梁的主流选择,桥梁服务器为什么会从“通用”走向“专用”过去十年,桥梁监控系统里的服务器大多沿用机架式通用服务器,它们承担数据采集、存储和简单的阈值报警,部署在桥梁管理处的机房中,但随着桥梁健……

    2026年10月20日
    200
  • 百度智能云登录失败怎么办?百度智能云账号密码忘了怎么找回

    百度智能云登录是访问其云计算、AI大模型及企业级服务的唯一入口,支持账号密码、手机验证码及百度生态账号一键授权,旨在保障企业数据资产安全并提升开发者协作效率,百度智能云登录入口与基础操作指南进入百度智能云控制台是开展一切云端业务的第一步,许多新用户往往在寻找正确入口时耗费时间,或者混淆了个人账号与企业账号的权限……

    2026年6月5日
    6300
  • https证书是什么?ssl证书申请多少钱

    HTTPS证书本质上是网站的安全身份证,它通过SSL/TLS协议在浏览器和服务器之间建立加密通道,确保数据不被窃取或篡改,同时向用户证明网站身份真实可靠,什么是SSL证书,为什么它被称为网站的“数字护照”如果把互联网比作一个繁忙的城市,那么网站就是其中的店铺,过去,店铺开门做生意,顾客直接走进来,交易过程完全透……

    2026年6月19日
    1910

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注