-
Notifications
You must be signed in to change notification settings - Fork 3.8k
2019 05 07 用Java实现JVM第六章《类和对象》
作者:小傅哥
博客:https://bugstack.cn - 原创系列专题
沉淀、分享、成长,让自己和他人都能有所收获!
本案例通过java代码实现jvm规范中指令集和解释器,完成后就可以开始执行1到100的加和计算。
Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码。我们已经知道,每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存在method_info结构的Code属性中。
- jdk 1.8.0
- IntelliJ IDEA Community Edition 2018.3.1 x64
- 调试配置
- 配置位置:Run/Debug Configurations -> program arguments
- 配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-06\target\test-classes\org\itstack\demo\test\HelloWorld
itstack-demo-jvm-06
├── pom.xml
└── src
└── main
│ └── java
│ └── org.itstack.demo.jvm
│ ├── classfile
│ │ ├── attributes {BootstrapMethods/Code/ConstantValue...}
│ │ ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...}
│ │ ├── ClassFile.java
│ │ ├── ClassReader.java
│ │ └── MemberInfo.java
│ ├── classpath
│ │ ├── impl
│ │ │ ├── CompositeEntry.java
│ │ │ ├── DirEntry.java
│ │ │ ├── WildcardEntry.java
│ │ │ └── ZipEntry.java
│ │ ├── Classpath.java
│ │ └── Entry.java
│ ├── classpath
│ │ ├── base
│ │ ├── comparisons
│ │ ├── constants
│ │ ├── control
│ │ ├── conversions
│ │ ├── extended
│ │ ├── loads
│ │ ├── math
│ │ │ ├── add
│ │ │ ├── and
│ │ │ ├── div
│ │ │ ├── iinc
│ │ │ ├── mul
│ │ │ ├── neg
│ │ │ ├── or
│ │ │ ├── rem
│ │ │ ├── sh
│ │ │ ├── sub
│ │ │ └── xor
│ │ ├── stack
│ │ ├── store
│ │ └── Factory
│ ├── rtda
│ │ ├── heap
│ │ │ ├── constantpool
│ │ │ ├── methodarea
│ │ │ │ ├── Class.java
│ │ │ │ ├── ClassMember.java
│ │ │ │ ├── Field.java
│ │ │ │ ├── Method.java
│ │ │ │ ├── Object.java
│ │ │ │ └── Slots.java
│ │ │ └── ClassLoader.java
│ │ ├── Frame.java
│ │ ├── JvmStack.java
│ │ ├── LocalVars.java
│ │ ├── OperandStack.java
│ │ ├── Slot.java
│ │ └── Thread.java
│ ├── Cmd.java
│ ├── Interpret.java
│ └── Main.java
└── test
└── java
└── org.itstack.demo.test
└── HelloWorld.java
Class.java
public class Class {
public int accessFlags;
public String name;
public String superClassName;
public String[] interfaceNames;
public RunTimeConstantPool runTimeConstantPool;
public Field[] fields;
public Method[] methods;
public ClassLoader loader;
public Class superClass;
public Class[] interfaces;
public int instanceSlotCount;
public int staticSlotCount;
public Slots staticVars;
public Class(ClassFile classFile) {
this.accessFlags = classFile.accessFlags();
this.name = classFile.className();
this.superClassName = classFile.superClassName();
this.interfaceNames = classFile.interfaceNames();
this.runTimeConstantPool = new RunTimeConstantPool(this, classFile.constantPool());
this.fields = new Field().newFields(this, classFile.fields());
this.methods = new Method().newMethods(this, classFile.methods());
}
public boolean isPublic() {
return 0 != (this.accessFlags & AccessFlags.ACC_PUBLIC);
}
public boolean isFinal() {
return 0 != (this.accessFlags & AccessFlags.ACC_FINAL);
}
public boolean isSuper() {
return 0 != (this.accessFlags & AccessFlags.ACC_SUPER);
}
public boolean isInterface() {
return 0 != (this.accessFlags & AccessFlags.ACC_INTERFACE);
}
public boolean isAbstract() {
return 0 != (this.accessFlags & AccessFlags.ACC_ABSTRACT);
}
public boolean isSynthetic() {
return 0 != (this.accessFlags & AccessFlags.ACC_SYNTHETIC);
}
public boolean isAnnotation() {
return 0 != (this.accessFlags & AccessFlags.ACC_ANNOTATION);
}
public boolean isEnum() {
return 0 != (this.accessFlags & AccessFlags.ACC_ENUM);
}
public RunTimeConstantPool constantPool() {
return this.runTimeConstantPool;
}
public Slots staticVars() {
return this.staticVars;
}
public boolean isAccessibleTo(Class other) {
return this.isPublic() || this.getPackageName().equals(other.getPackageName());
}
public String getPackageName() {
int i = this.name.lastIndexOf("/");
if (i >= 0) return this.name;
return "";
}
public Method getMainMethod() {
return this.getStaticMethod("main", "([Ljava/lang/String;)V");
}
private Method getStaticMethod(String name, String descriptor) {
for (Method method : this.methods) {
if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
return method;
}
}
return null;
}
public Object newObject() {
return new Object(this);
}
public boolean isAssignableFrom(Class other) {
if (this == other) return true;
if (!other.isInterface()) {
return this.isSubClassOf(other);
} else {
return this.isImplements(other);
}
}
public boolean isSubClassOf(Class other) {
for (Class c = this.superClass; c != null; c = c.superClass) {
if (c == other) {
return true;
}
}
return false;
}
private boolean isImplements(Class other) {
for (Class c = this; c != null; c = c.superClass) {
for (Class clazz : c.interfaces) {
if (clazz == other || clazz.isSubInterfaceOf(other)) {
return true;
}
}
}
return false;
}
public boolean isSubInterfaceOf(Class iface) {
for (Class superInterface : this.interfaces) {
if (superInterface == iface || superInterface.isSubInterfaceOf(iface)) {
return true;
}
}
return false;
}
}
ClassMember.java
public class ClassMember {
public int accessFlags;
public String name;
public String descriptor;
public Class clazz;
public void copyMemberInfo(MemberInfo memberInfo) {
this.accessFlags = memberInfo.accessFlags();
this.name = memberInfo.name();
this.descriptor = memberInfo.descriptor();
}
public boolean isPublic() {
return 0 != (this.accessFlags & AccessFlags.ACC_PUBLIC);
}
public boolean isPrivate() {
return 0 != (this.accessFlags & AccessFlags.ACC_PRIVATE);
}
public boolean isProtected() {
return 0 != (this.accessFlags & AccessFlags.ACC_PROTECTED);
}
public boolean isStatic() {
return 0 != (this.accessFlags & AccessFlags.ACC_STATIC);
}
public boolean isFinal() {
return 0 != (this.accessFlags & AccessFlags.ACC_FINAL);
}
public boolean isSynthetic() {
return 0 != (this.accessFlags & AccessFlags.ACC_SYNTHETIC);
}
public String name() {
return this.name;
}
public String descriptor() {
return this.descriptor;
}
public Class clazz() {
return this.clazz;
}
public boolean isAccessibleTo(Class d) {
if (this.isPublic()) {
return true;
}
Class c = this.clazz;
if (this.isProtected()) {
return d == c || c.getPackageName().equals(d.getPackageName());
}
if (!this.isPrivate()) {
return c.getPackageName().equals(d.getPackageName());
}
return d == c;
}
}
Field.java
package org.itstack.demo.jvm.rtda.heap.methodarea;
import org.itstack.demo.jvm.classfile.MemberInfo;
import org.itstack.demo.jvm.classfile.attributes.impl.ConstantValueAttribute;
import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;
public class Field extends ClassMember {
public int constValueIndex;
public int slotId;
public Field[] newFields(Class clazz, MemberInfo[] cfFields) {
Field[] fields = new Field[cfFields.length];
for (int i = 0; i < cfFields.length; i++) {
fields[i] = new Field();
fields[i].clazz = clazz;
fields[i].copyMemberInfo(cfFields[i]);
fields[i].copyAttributes(cfFields[i]);
}
return fields;
}
public void copyAttributes(MemberInfo cfField) {
ConstantValueAttribute valAttr = cfField.ConstantValueAttribute();
if (null != valAttr) {
this.constValueIndex = valAttr.constantValueIdx();
}
}
public boolean isVolatile() {
return 0 != (this.accessFlags & AccessFlags.ACC_VOLATILE);
}
public boolean isTransient() {
return 0 != (this.accessFlags & AccessFlags.ACC_TRANSIENT);
}
public boolean isEnum() {
return 0 != (this.accessFlags & AccessFlags.ACC_ENUM);
}
public int constValueIndex() {
return this.constValueIndex;
}
public int slotId() {
return this.slotId;
}
public boolean isLongOrDouble() {
return this.descriptor.equals("J") || this.descriptor.equals("D");
}
}
Method.java
package org.itstack.demo.jvm.rtda.heap.methodarea;
import org.itstack.demo.jvm.classfile.MemberInfo;
import org.itstack.demo.jvm.classfile.attributes.impl.CodeAttribute;
import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;
public class Method extends ClassMember {
public int maxStack;
public int maxLocals;
public byte[] code;
public Method[] newMethods(Class clazz, MemberInfo[] cfMethods) {
Method[] methods = new Method[cfMethods.length];
for (int i = 0; i < cfMethods.length; i++) {
methods[i] = new Method();
methods[i].clazz = clazz;
methods[i].copyMemberInfo(cfMethods[i]);
methods[i].copyAttributes(cfMethods[i]);
}
return methods;
}
public void copyAttributes(MemberInfo cfMethod) {
CodeAttribute codeAttr = cfMethod.codeAttribute();
if (null != codeAttr) {
this.maxStack = codeAttr.maxStack();
this.maxLocals = codeAttr.maxLocals();
this.code = codeAttr.data();
}
}
public boolean isSynchronized() {
return 0 != (this.accessFlags & AccessFlags.ACC_SYNCHRONIZED);
}
public boolean isBridge() {
return 0 != (this.accessFlags & AccessFlags.ACC_BRIDGE);
}
public boolean isVarargs() {
return 0 != (this.accessFlags & AccessFlags.ACC_VARARGS);
}
public boolean isNative() {
return 0 != (this.accessFlags & AccessFlags.ACC_NATIVE);
}
public boolean isAbstract() {
return 0 != (this.accessFlags & AccessFlags.ACC_ABSTRACT);
}
public boolean isStrict() {
return 0 != (this.accessFlags & AccessFlags.ACC_STRICT);
}
public int maxStack() {
return this.maxStack;
}
public int maxLocals() {
return this.maxLocals;
}
public byte[] code() {
return this.code;
}
}
Object.java
package org.itstack.demo.jvm.rtda.heap.methodarea;
public class Object {
Class clazz;
Slots fields;
public Object(Class clazz){
this.clazz = clazz;
this.fields = new Slots(clazz.instanceSlotCount);
}
public Class clazz(){
return this.clazz;
}
public Slots fields(){
return this.fields;
}
public boolean isInstanceOf(Class clazz){
return clazz.isAssignableFrom(this.clazz);
}
}
Slots.java
package org.itstack.demo.jvm.rtda.heap.methodarea;
import org.itstack.demo.jvm.rtda.Slot;
public class Slots {
private Slot[] slots;
public Slots(int slotCount) {
if (slotCount > 0) {
slots = new Slot[slotCount];
for (int i = 0; i < slotCount; i++) {
slots[i] = new Slot();
}
}
}
public void setInt(int idx, int val) {
this.slots[idx].num = val;
}
public int getInt(int idx) {
return this.slots[idx].num;
}
public void setFloat(int idx, float val) {
this.slots[idx].num = (int) val;
}
public float getFloat(int idx) {
return this.slots[idx].num;
}
public void setLong(int idx, long val) {
this.slots[idx].num = (int) val;
this.slots[idx + 1].num = (int) (val >> 32);
}
public long getLong(int idx) {
int low = this.slots[idx].num;
int high = this.slots[idx + 1].num;
return (long) high << 32 | (long) low;
}
public void setDouble(int idx, double val) {
this.setLong(idx, (long) val);
}
public Double getDouble(int idx) {
return (double) this.getLong(idx);
}
public void setRef(int idx, Object ref) {
this.slots[idx].ref = ref;
}
public Object getRef(int idx){
return this.slots[idx].ref;
}
}
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\lib\idea_rt.jar=61887:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\classes;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\alibaba\fastjson\1.2.40\fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld
classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld args:null
寄存器(指令):0x03 -> ICONST_0 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x04 -> ICONST_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x3d -> ISTORE_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
寄存器(指令):0x1b -> ILOAD_1 => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":0},{"num":100}] 操作数栈:[{"num":0},{"num":100}]
... ...
寄存器(指令):0x60 -> IADD => 局部变量表:[{"num":4950},{"num":100}] 操作数栈:[{"num":4950},{"num":100}]
寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x84 -> IINC => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0xa7 -> GOTO => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
5050
寄存器(指令):0xb1 -> RETURN => 局部变量表:[{"num":101},{"num":5050}] 操作数栈:[{"num":101},{"num":5050}]
Exception in thread "main" java.lang.RuntimeException: jvm stack is empty!
at org.itstack.demo.jvm.rtda.JvmStack.pop(JvmStack.java:33)
微信搜索「bugstack虫洞栈」公众号,关注后回复「用Java实现jvm源码」获取本文源码&更多原创专题案例!
小傅哥(微信:fustack),公众号:bugstack虫洞栈
| bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
🌏 知识星球:码农会锁
实战项目:「DDD+RPC分布式抽奖系统
」、专属小册、问题解答、简历指导、架构图稿、视频课程
🐲 头条
-
💥
🎁 Lottery 抽奖系统
- 基于领域驱动设计的四层架构的互联网分布式开发实践 -
小傅哥的《重学 Java 设计模式》
- 全书彩印、重绘类图、添加内容 -
⭐小傅哥的《Java 面经手册》
- 全书5章29节,417页11.5万字,完稿&发版 -
小傅哥的《手撸 Spring》
- 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理 -
🌈小傅哥的《SpringBoot 中间件设计和开发》
- 小册16个中间件开发30个代码库
⛳ 目录
💋 精选
🐾 友链
建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。
作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈
2021年10月24日,小傅哥
的文章全部开源到代码库 CodeGuide
中,与同好同行,一起进步,共同维护。
这里我提供 3 种方式:
-
提出
Issue
:在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交PR
)。 -
处理
Issue
: 帮忙处理一些待处理的Issue
。 -
提交
PR
: 对于错别字/笔误这类问题可以直接提交PR
,无需提交Issue
确认。
详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹
- 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
- 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。