前言
首先概括一下 JVM 运行流程:
JVM 初始化
加载主类
运行 Main 方法
加载其他类
运行其他方法
GC 开始
JVM 初始化
JVM 的入口位于 /mini_jvm/main.c ----- main() 方法
main 方法的主要任务是:
- 解析参数并保存留到后面初始化的时候读取
- 最后带着参数 List 进入到真正的入口:
ret = execute_jvm(classpath, main_name, java_para);
jvm.c ----- execute_jvm()
这个方法干了两件事:
- 初始化 JVM
- 拼接 java 的 main 方法签名调用 main 方法:
s32 execute_jvm(c8 *p_classpath, c8 *p_mainclass, ArrayList *java_para) {
//初始化
jvm_init(p_classpath, NULL);
//拼装 Main 方法的签名
c8 *p_methodname = "main";
c8 *p_methodtype = "([Ljava/lang/String;)V";
//call main
s32 ret = call_method_main(p_mainclass, p_methodname, p_methodtype, java_para);
jvm_destroy(NULL);
return ret;
}
jvm.c ---- jvm_init()
- 初始化异常信号,这些底层信号捕捉以后可能会送到 Java 层抛出异常
- 初始化基础的 JNI 方法对应表
- 创建线程容器
- GC 初始化
- 各种库初始化和注册到 jni 映射表 ( native/java 标准库/net/debug 库)
- 创建系统 ClassLoader(SystemClassLoader)
- 装入本地平台的一些系统属性,可以在下面看到一些我们在 Java 里面比较熟悉的属性
#if __JVM_OS_MAC__
sys_properties_set_c("os.name", "Mac");
sys_properties_set_c("path.separator", ":");
sys_properties_set_c("file.separator", "/");
sys_properties_set_c("line.separator", "\n");
#elif __JVM_OS_LINUX__
sys_properties_set_c("os.name","Linux");
sys_properties_set_c("path.separator",":");
sys_properties_set_c("file.separator","/");
sys_properties_set_c("line.separator", "\n");
#elif __JVM_OS_MINGW__ || __JVM_OS_CYGWIN__ || __JVM_OS_VS__
sys_properties_set_c("os.name","Windows");
sys_properties_set_c("path.separator",";");
sys_properties_set_c("file.separator","\\");
sys_properties_set_c("line.separator", "\r\n");
#endif
- 启动调试
void jvm_init(c8 *p_classpath, StaticLibRegFunc regFunc) {
//初始化一堆异常信号
signal(SIGABRT, _on_jvm_sig);
.....
//初始化 JNIENV native 方法
init_jni_func_table();
//创建线程容器
thread_list = arraylist_create(0);
//创建垃圾收集器
garbage_collector_create();
//启动垃圾回收
garbage_thread_resume();
memset(&jvm_runtime_cache, 0, sizeof(OptimizeCache));
//本地方法库
native_libs = arraylist_create(0);
//std libc
reg_std_native_lib();
//net lib
reg_net_native_lib();
//debug lib
reg_jdwp_native_lib();
if (regFunc)regFunc(&jnienv);//register static lib
//创建系统类加载器
sys_classloader = classloader_create(p_classpath);
//装入系统属性
sys_properties_load(sys_classloader);
//启动调试器
jdwp_start_server();
}
类加载
类加载器
类加载需要类加载器 ClassLoader
数据结构:
struct _ClassLoader {
//类路径集合
ArrayList *classpath;
//所有加载的类
Hashtable *classes;
//String 对象常量池,根据设定,所有 stream 常量统一存在加载他的 ClassLoader 中
Hashtable *table_jstring_const;
//同步锁
spinlock_t lock;
};
类加载器的创建:
jvm.c ----- classloader_create()
参数是 classpath ---- 这是一个未分隔的字符串,是 class path 的列表字符串
方法大致流程 :
- 首先分配 class_loader 结构体的内存
- 给新 class_loader 上锁
- 分隔 class_path 字符串保存到 List
- class path 列表保存到 class_laoder
- 创建 String 常量池
ClassLoader *classloader_create(c8 *path) {
// 分派内存
ClassLoader *class_loader = jvm_calloc(sizeof(ClassLoader));
// 类加载同步锁
spin_init(&class_loader->lock, 0);
//解析 classpath 参数,分割存储
class_loader->classpath = arraylist_create(0);
Utf8String *g_classpath = utf8_create_c(path);
Utf8String *tmp = NULL;
s32 i = 0;
while (i < g_classpath->length) {
......
}
......
//创建类容器
class_loader->classes = hashtable_create(UNICODE_STR_HASH_FUNC, UNICODE_STR_EQUALS_FUNC);
//创建jstring 相关容器
class_loader->table_jstring_const = hashtable_create(UNICODE_STR_HASH_FUNC, UNICODE_STR_EQUALS_FUNC);
return class_loader;
}
加载类的流程
jvm.c ------ load_class()
- 首先将用户输入的限定名格式 xxx.xxx.xxx -> xxx/xxx/xxx
- 尝试从已加载的类集合中读取
- 若没有加载则开始加载类
- 读取字节码到内存中
- 解析字节码
//类加载
s32 load_class(ClassLoader *loader, Utf8String *pClassName, Runtime *runtime) {
if (!loader)return 0;
s32 iret = 0;
//class name
Utf8String *clsName = utf8_create_copy(pClassName);
// byte code 中都为 /.../...
utf8_replace_c(clsName, ".", "/");
//先从缓存中读取
JClass *tmpclazz = classes_get(clsName);
//如果是数组类型
if (utf8_indexof_c(clsName, "[") == 0) {
//先从缓存中读
tmpclazz = array_class_create_get(runtime, clsName);
}
//如果缓存空,开始真正 load class
if (!tmpclazz) {
ByteBuf *bytebuf = NULL;
utf8_append_c(clsName, ".class");
// 读字节码到内存中
bytebuf = load_file_from_classpath(loader, clsName);
if (bytebuf != NULL) {
// 解析字节码 -> JClass
tmpclazz = resole_class(bytebuf, runtime);
bytebuf_destory(bytebuf);
}
}
if (!tmpclazz) {
jvm_printf("class not found: %s \n", utf8_cstr(clsName));
}
utf8_destory(clsName);
return iret;
}
jvm.c ----- load_file_from_classpath()
读取类的字节码到内存:
读取分为两种情况:
- 从文件夹中直接读取 .class 文件,这时候直接以二进制方式全部读取即可
- 从 jar 包中读取,这时候需要解压到内存中,这里使用 utils 中的 miniz 解压,这也是个开源的库
ByteBuf *load_file_from_classpath(ClassLoader *loader, Utf8String *path) {
ByteBuf *bytebuf = NULL;
s32 i, iret;
for (i = 0; i < loader->classpath->length; i++) {
Utf8String *pClassPath = arraylist_get_value(loader->classpath, i);
if (isDir(pClassPath)) { //form file
......
//读取原文件到内存
iret = _loadFileContents(utf8_cstr(filepath), bytebuf);
......
} else { //from jar
......
iret = zip_loadfile(utf8_cstr(pClassPath), utf8_cstr(path), bytebuf);
......
}
}
return bytebuf;
}
运行时类结构
jvm.h ----- JClass(_ClassType)
- 运行时类结构首先在 Java 世界中他是一个对象(Class 对象),可以看到他有 MemoryBlock,在 Runtime 中他被当作一种对象管理
- 他描述了运行时 Class 所需要用到的一切信息
- 他加载成功后便会添加到 GC Holder,即不会被 GC 回收
struct _ClassType {
//内存块描述头部
MemoryBlock mb;
JClass *superclass;//父类
__refer *constant_item_ptr;//存放常量池项目地址
s32 constant_item_count;//总数
//类变量及实例变量的参数
s32 field_instance_start;//实例变量模板起始起址,继承自父类的变量放在前面
s32 field_instance_len; //非静态变量长度
s32 field_static_len; //静态变量内存长度
c8 *field_static; //静态变量内存地址
//Class 和 ClassLoader 的实例,随 class 的结构体生灭
Instance *ins_class; //object of java.lang.Class
Instance *jClassLoader;// java classloader
//public:
s32 (*_load_class_from_bytes)(struct _ClassType *_this, ByteBuf *buf);
//源文件名
Utf8String *source;
BootstrapMethodsAttr *bootstrapMethodAttr;
//类名
Utf8String *name;
//finaliz 方法
MethodInfo *finalizeMethod;
ClassFileFormat cff;
//常量池
ConstantPool constantPool;
//接口
InterfacePool interfacePool;
//成员变量
FieldPool fieldPool;
//方法
MethodPool methodPool;
//属性
AttributePool attributePool;
//for array class 这些都是为了加速数组类型的
Pairlist *arr_class_type;//for object array create speedup,left is utf8 index of class, right is arr class
ArrayList *insFieldPtrIndex;//for optmize , save object pointer field index
ArrayList *staticFieldPtrIndex; //save static field index
s8 status;
//基础类型的包装类
u8 primitive;//primitive data type int/long/short/char/short/byte/float/double
};
解析类结构
class_loader.c ------ resole_class()
- 首先依然是分配 JClass 的内存
- 解析字节码
- 类初始化,调用 static 块方法等
- 将 Class 对象添加到常量池,防止被 GC
//解析类
JClass *resole_class(ByteBuf *bytebuf, Runtime *runtime) {
JClass *tmpclazz = NULL;
if (bytebuf != NULL) {
//分配内存
tmpclazz = class_create(runtime);
// 解析字节码
s32 iret = tmpclazz->_load_class_from_bytes(tmpclazz, bytebuf);//load file
if (iret == 0) {
//放到缓存中
classes_put(tmpclazz);
//准备工作,初始化调用静态块等
class_prepar(tmpclazz, runtime);
//将 Class 对象添加到常量池(这里指的是不会被 GC 的地方)
gc_refer_hold(tmpclazz);
#if _JVM_DEBUG_BYTECODE_DETAIL > 5
jvm_printf("load class: %s \n", utf8_cstr(clsName));
#endif
} else {
class_destory(tmpclazz);
tmpclazz = NULL;
}
}
return tmpclazz;
}
.Class 字节码结构
类型 | 名称 | 数量 |
---|---|---|
u4 | magic(魔术) | 1 |
u2 | minor_version(次版本号) | 1 |
u2 | major_version(主版本号) | 1 |
u2 | constant_pool_count(常量个数) | 1 |
cp_info | constant_pool(常量池表) | constant_pool_count-1 |
u2 | access_flags(类的访问控制权限) | 1 |
u2 | this_class(类名) | 1 |
u2 | super_class(父类名) | 1 |
u2 | interfaces_count(接口个数) | 1 |
u2 | interfaces(接口名) | interfaces_count |
u2 | fields_count(域个数) | 1 |
field_info | fields(域的表) | fields_count |
u2 | methods_count(方法的个数) | 1 |
method_info | methods(方法表) | methods_count |
u2 | attributes_count(附加属性的个数) | 1 |
attribute_info | attributes(附加属性的表) | attributes_count |
Code
/* Java Class File */
typedef struct _ClassFileFormat {
//魔数
u8 magic_number[4];
u16 minor_version;
u16 major_version;
//常量数量
u16 constant_pool_count;
/* constant pool */
//访问标志 private 这些
u16 access_flags;
//本类类名 index
u16 this_class;
//父类类名 index
u16 super_class;
//接口数量
u16 interface_count;
/* interfaceRef pool */
//类成员变量数量
u16 fields_count;
/* obj_fields pool */
//方法数量
u16 methods_count;
/* methodRef pool */
//字段附加属性的个数
u16 attributes_count;
/* attributes pool */
} ClassFileFormat;
常量
常量是字节码文件中的重要组成部分,集中在常量池中,字节码解析的最重要步骤之一,就是将这些常量解析出来,并且归类存到内存中的常量池中去。
常量类型
字节码的常量有以下类型:
- UTF-8 String
- Integer
- Float
- Long
- Double
- Class 引用(Class 的全限定名)
- String 引用(全限定名)
- Field 引用(全限定名)
- Method 引用(全限定名)
- Interface(接口) methodRef(方法) garbage_refer(全限定名)
- Name and type descriptor(签名)
可以总结为
- 基本数据类型的常量,字符串/数字等
- 引用常量,这里是指引用对象的全限定名,类似 Lcom/xxx/xxx
- 类型签名
enum {
CONSTANT_UTF8 = 1,
CONSTANT_INTEGER = 3,
CONSTANT_FLOAT = 4,
CONSTANT_LONG = 5,
CONSTANT_DOUBLE = 6,
CONSTANT_CLASS = 7,
CONSTANT_STRING_REF = 8,
CONSTANT_FIELD_REF = 9,
CONSTANT_METHOD_REF = 10,
CONSTANT_INTERFACE_METHOD_REF = 11,
CONSTANT_NAME_AND_TYPE = 12,
CONSTANT_METHOD_HANDLE = 15,
CONSTANT_METHOD_TYPE = 16,
CONSTANT_INVOKE_DYNAMIC = 18,
};
常量池
//常量池结构体
typedef struct _ConstantPool {
ArrayList *utf8CP;
ArrayList *classRef;
ArrayList *stringRef;
ArrayList *fieldRef;
ArrayList *methodRef;
ArrayList *interfaceMethodRef;
} ConstantPool;
基本类型常量
//UTF-8 字符串常量
typedef struct _ConstantUTF8 {
ConstantItem item;
u16 string_size;
//
Utf8String *utfstr;
Instance *jstr;
} ConstantUTF8;
//Int 常量
typedef struct _ConstantInteger {
ConstantItem item;
s32 value;
} ConstantInteger;
typedef struct _ConstantFloat {
ConstantItem item;
f32 value;
} ConstantFloat;
typedef struct _ConstantLong {
ConstantItem item;
s64 value;
} ConstantLong;
typedef struct _ConstantDouble {
ConstantItem item;
f64 value;
} ConstantDouble;
引用常量
//类引用常量,类全限定名
typedef struct _ConstantClassRef {
ConstantItem item;
u16 stringIndex;
//
Utf8String *name;
JClass *clazz;
} ConstantClassRef;
//String 引用常量
typedef struct _ConstantStringRef {
ConstantItem item;
u16 stringIndex;
} ConstantStringRef;
//Field 引用常量
typedef struct _ConstantFieldRef {
ConstantItem item;
u16 classIndex;
u16 nameAndTypeIndex;
FieldInfo *fieldInfo;
} ConstantFieldRef;
//方法引用 常量,包含信息较多
typedef struct _ConstantMethodRef {
ConstantItem item;
//类 index
u16 classIndex;
u16 nameAndTypeIndex;
//方法结构体
MethodInfo *methodInfo;
s32 para_slots;
ConstantNameAndType *nameAndType;
//方法签名信息
Utf8String *name;
Utf8String *descriptor;
Utf8String *clsName;
//虚方法列表
Pairlist *virtual_methods;
} ConstantMethodRef, ConstantInterfaceMethodRef;
//接口池常量
typedef struct _InterfacePool {
ConstantClassRef *clasz;
s32 clasz_used;
} InterfacePool;
//方法句柄常量
typedef struct _ConstantMethodHandle {
ConstantItem item;
u8 reference_kind;
u16 reference_index;
} ConstantMethodHandle;
访问标记
这个没什么好说的
//访问标志
enum {
ACC_PUBLIC = 0x0001,
ACC_PRIVATE = 0x0002,
ACC_PROTECTED = 0x0004,
ACC_STATIC = 0x0008,
ACC_FINAL = 0x0010,
ACC_SYNCHRONIZED = 0x0020,
ACC_VOLATILE = 0x0040,
ACC_TRANSIENT = 0x0080,
ACC_NATIVE = 0x0100,
ACC_INTERFACE = 0x0200,
ACC_ABSTRACT = 0x0400,
ACC_STRICT = 0x0800,
};
属性
属性是字节码中另外一个组成部分
包括 Code 行号 异常表等
//属性
typedef struct _AttributeInfo {
u16 attribute_name_index;
s32 attribute_length;
u8 *info;
} AttributeInfo;
Code 属性
Code 属性保存了字节码中的可执行部分,即指令流
首先看一下 Oracle 的文档对 Code 属性结构的描述:
1.attribute_name_index
The value of the attribute_name_index item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Utf8_info structure (§4.4.7) representing the string “Code”.
2.attribute_length
The value of the attribute_length item indicates the length of the attribute, excluding the initial six bytes.
3.max_stack
The value of the max_stack item gives the maximum depth of the operand stack of this method (§2.6.2) at any point during execution of the method.
4.max_locals
The value of the max_locals item gives the number of local variables in the local variable array allocated upon invocation of this method (§2.6.1), including the local variables used to pass parameters to the method on its invocation.
The greatest local variable index for a value of type long or double is max_locals - 2. The greatest local variable index for a value of any other type is max_locals - 1.
5.code_length
The value of the code_length item gives the number of bytes in the code array for this method.
The value of code_length must be greater than zero (as the code array must not be empty) and less than 65536.
6.code[]
The code array gives the actual bytes of Java Virtual Machine code that implement the method.
When the code array is read into memory on a byte-addressable machine, if the first byte of the array is aligned on a 4-byte boundary, the tableswitch and lookupswitch 32-bit offsets will be 4-byte aligned. (Refer to the descriptions of those instructions for more information on the consequences of code array alignment.)
The detailed constraints on the contents of the code array are extensive and are given in a separate section (§4.9).
6.exception_table_length
The value of the exception_table_length item gives the number of entries in the exception_table table.
7.exception_table[]
Each entry in the exception_table array describes one exception handler in the code array. The order of the handlers in the exception_table array is significant (§2.10).
Each exception_table entry contains the following four items:
8.start_pc, end_pc
代表方法作用域
The values of the two items start_pc and end_pc indicate the ranges in the code array at which the exception handler is active. The value of start_pc must be a valid index into the code array of the opcode of an instruction. The value of end_pc either must be a valid index into the code array of the opcode of an instruction or must be equal to code_length, the length of the code array. The value of start_pc must be less than the value of end_pc.
The start_pc is inclusive and end_pc is exclusive; that is, the exception handler must be active while the program counter is within the interval [start_pc, end_pc).
The fact that end_pc is exclusive is a historical mistake in the design of the Java Virtual Machine: if the Java Virtual Machine code for a method is exactly 65535 bytes long and ends with an instruction that is 1 byte long, then that instruction cannot be protected by an exception handler. A compiler writer can work around this bug by limiting the maximum size of the generated Java Virtual Machine code for any method, instance initialization method, or static initializer (the size of any code array) to 65534 bytes.
9.handler_pc
The value of the handler_pc item indicates the start of the exception handler. The value of the item must be a valid index into the code array and must be the index of the opcode of an instruction.
10.catch_type
If the value of the catch_type item is nonzero, it must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing a class of exceptions that this exception handler is designated to catch. The exception handler will be called only if the thrown exception is an instance of the given class or one of its subclasses.
The verifier checks that the class is Throwable or a subclass of Throwable (§4.9.2).
If the value of the catch_type item is zero, this exception handler is called for all exceptions.
This is used to implement finally (§3.13).
11.attributes_count
The value of the attributes_count item indicates the number of attributes of the Code attribute.
12.attributes[]
Each value of the attributes table must be an attribute_info structure (§4.7).
A Code attribute can have any number of optional attributes associated with it.
The attributes defined by this specification as appearing in the attributes table of a Code attribute are listed in Table 4.7-C.
The rules concerning attributes defined to appear in the attributes table of a Code attribute are given in §4.7.
The rules concerning non-predefined attributes in the attributes table of a Code attribute are given in §4.7.1.
结构体:
struct _CodeAttribute {
u16 attribute_name_index;
s32 attribute_length;
//方法所需要的最大栈大小
u16 max_stack;
//方法所有的最大本地变量
u16 max_locals;
//code 长度
s32 code_length;
//code 数组
u8 *code; // [code_length]; -> 相当于 code[code_lenth],每行 code 占 4 字节
//异常表长度
u16 exception_table_length;
ExceptionTable *exception_table; //[exception_table_length]; -> 相当于 exception_table[exception_table_length],每行 code 占 8 字节
//行号
u16 line_number_table_length;
LineNumberTable *line_number_table;
//本地变量,结构同上
u16 local_var_table_length;
LocalVarTable *local_var_table;
};
行号属性
记录了每行指令所在源代码的行号,主要服务于 Debug
记录 PC 指针对应的源码行号
//行号
typedef struct _line_number {
u16 start_pc;
u16 line_number;
} LineNumberTable;
异常向量表属性
记录异常的分支跳转表
//异常表
typedef struct _ExceptionTable {
//异常开始结束的 PC 指针
u16 start_pc;
u16 end_pc;
//异常处理函数开始的 PC 指针
u16 handler_pc;
//异常捕获的类型
u16 catch_type;
} ExceptionTable;
本地变量属性
本地变量记录了代码块运行时所需要的本地变量和其属性
- 该属性的长度还会决定运行时方法堆栈的大小
- 该属性内部会有很多 index,这些 index 指向常量池,解析时候对应常量池就能还原出完整的本地变量信息
//本地变量表,决定方法栈大小
typedef struct _LocalVarTable {
u16 start_pc;
u16 length;
u16 name_index;
u16 descriptor_index;
u16 index;
} LocalVarTable;
Bootstrap 方法属性
这个和 JDK8 新加入的 invoke_dynamic 相关
现在主要用于实现 lambda 表达式,和函数指针
//invoke dynamic 所调用的方法,一般用于 lambada 表达式
typedef struct _BootstrapMethod {
u16 bootstrap_method_ref;
u16 num_bootstrap_arguments;
u16 *bootstrap_arguments;
//cache
MethodInfo *make;
} BootstrapMethod;
typedef struct BootstrapMethods_attribute {
u16 num_bootstrap_methods;
BootstrapMethod *bootstrap_methods;
} BootstrapMethodsAttr;
方法
有了上面的 常量,属性,就能在解析的时候拼凑出类的成员方法和成员变量等基本元素
struct _MethodInfo {
u16 access_flags;
//方法名在常量池中的 index
u16 name_index;
//方法描述在常量池中的 index
u16 descriptor_index;
//属性数量
u16 attributes_count;
//属性链表
AttributeInfo *attributes;
//方法属性
CodeAttribute *converted_code;
//方法参数的各种偏移
MethodParaOffset *paraOffset;
//link
//在常量池中取值的结果
Utf8String *name;
Utf8String *descriptor;
Utf8String *paraType;
//返回值类型
Utf8String *returnType;
//方法所在类
JClass *_this_class;
//如果是 native 方法,则其对应的指针
java_native_fun native_func;
//断点
Pairlist *breakpoint;
s16 para_slots;
s16 para_count_with_this;
};
方法参数偏移
- 这个主要记录方法参数在方法栈中的偏移地址
- 用于在运行时让解释器准确定位到对应方法参数
typedef struct _MethodParaOffset {
s32 stackOffset;
s16 localOffset;
s16 byteCount;
} MethodParaOffset;
Field
内容和 Method 类似,这里就不多赘述了
struct _FieldInfo {
u16 access_flags;
u16 name_index;
u16 descriptor_index;
u16 attributes_count;
//属性
AttributeInfo *attributes;
//link
Utf8String *name;
Utf8String *descriptor;
//所在类
JClass *_this_class;
//字段的偏移地址,静态字段存放在class中
u16 offset;
//非静态字段在 Instancce 中的偏移
u16 offset_instance;
//数据类型
u8 datatype_idx;
//是否是引用
u8 isrefer;
u8 datatype_bytes;
//是否是原子的
u8 isvolatile;
};
typedef struct _FieldPool {
FieldInfo *field;
s32 field_used;
} FieldPool;
接口
接口只是一个类引用常量的集合
//接口池
typedef struct _InterfacePool {
ConstantClassRef *clasz;
s32 clasz_used;
} InterfacePool;
字节码解析
class_loader.c -----_LOAD_CLASS_FROM_BYTES()
依次解析字节码中的各个元素,每个流程都差不多
- 首先读取元素个数
- 然后根据个数一个个去解析
//解析 Class 字节码
/* Parse Class File */
s32 _LOAD_CLASS_FROM_BYTES(JClass *_this, ByteBuf *buf) {
ClassFileFormat *cff = &(_this->cff);
//读魔数
/* magic number */
bytebuf_read_batch(buf, (c8 *) &cff->magic_number, 4);
// fread(cff->magic_number, 4, 1, fp);
//读取版本
/* minor_version */
//fread(short_tmp, 2, 1, fp);
Short2Char s2c;
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->minor_version = s2c.s;
/* major_version */
//fread(short_tmp, 2, 1, fp);
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->major_version = s2c.s;
//常量池
/* constant pool */
//fread(short_tmp, 2, 1, fp);
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->constant_pool_count = s2c.s;
/* constant pool table */
_parse_constant_pool(_this, buf, cff->constant_pool_count);
/* access flag */
//fread(short_tmp, 2, 1, fp);
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->access_flags = s2c.s;
/* this class */
//fread(short_tmp, 2, 1, fp);
//解析外部类
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->this_class = s2c.s;
/* super class */
//fread(short_tmp, 2, 1, fp);
//解析父类
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->super_class = s2c.s;
/* interfaceRef count */
//fread(short_tmp, 2, 1, fp);
//获取接口个数
s2c.c1 = (c8) bytebuf_read(buf);
s2c.c0 = (c8) bytebuf_read(buf);
cff->interface_count = s2c.s;
//解析接口限定名
/* interfaceRef pool table */
_parse_interface_pool(_this, buf, cff->interface_count);
//获取 Feild 个数
.....
//解析 Field
_parse_field_pool(_this, buf, cff->fields_count);
//获取方法个数
.....
//解析方法
_parse_method_pool(_this, buf, cff->methods_count);
//解析类属性
.....
_parse_attribute_pool(_this, buf, cff->attributes_count);
//fclose(fp);
//优化
_class_optimize(_this);
_this->status = CLASS_STATUS_LOADED;
return 0;
}
这里要注意的是 Short2Char,Int2Char。。。等类似结构
这些是用来解决不同平台大小端区别的,巧妙的使用了联合体的特性
以 Short2Char 为例:
//大小端 byte -> 目标类型
#if __JVM_LITTLE_ENDIAN__
//小端
typedef union _Short2Char {
s16 s;
struct {
c8 c0;
c8 c1;
};
} Short2Char;
//大端
#elif __JVM_BIG_ENDIAN__
typedef union _Short2Char {
s16 s;
struct {
c8 c1;
c8 c0;
};
} Short2Char;
常量池解析
根据常量类型分发给各个函数处理
这里传入的参数有读到的常量 index
还有 ByteBuf 指针,ByteBuf 会记录最后一次读取的位置,所以下一次读取常量就是紧接着上一次读取的位置
s32 bytebuf_read(ByteBuf *bf) {
if (bf->rp + 1 > bf->wp) {
return -1;
}
//注意这里的 rp ++
s32 i = (u8) bf->buf[bf->rp++];
return i;
}
//常量池解析
s32 _parse_constant_pool(JClass *_this, ByteBuf *buf, s32 count) {
u8 tag = 0;
s32 i = 0;
u64 offset_start = 0;
u64 offset_end = 0;
_this->constant_item_ptr = jvm_calloc(count * sizeof(void *));//测量该平台指针长度
_this->constant_item_count = count;
for (i = 1; i < count; i++) {
//fread(&tag, 1, 1, fp);
tag = (u8) bytebuf_read(buf);
offset_start = buf->rp;
//jvm_printf("!!!read tag = %02x!!!\n", tag);
__refer ptr = NULL;
s32 idx = i;
switch (tag) {
case CONSTANT_UTF8:
ptr = _parseCPString(_this, buf, i);
break;
case CONSTANT_INTEGER:
ptr = _parseCPInteger(_this, buf, i);
break;
case CONSTANT_FLOAT:
ptr = _parseCPFloat(_this, buf, i);
break;
case CONSTANT_LONG:
ptr = _parseCPLong(_this, buf, i);
i++;
break;
case CONSTANT_DOUBLE:
ptr = _parseCPDouble(_this, buf, i);
i++;
break;
case CONSTANT_STRING_REF:
ptr = _parseCPStringRef(_this, buf, i);
break;
case CONSTANT_CLASS:
ptr = _parseCPClass(_this, buf, i);
break;
case CONSTANT_FIELD_REF:
ptr = _parseCPField(_this, buf, i);
break;
case CONSTANT_METHOD_REF:
ptr = _parseCPMethod(_this, buf, i);
break;
case CONSTANT_INTERFACE_METHOD_REF:
ptr = _parseCPMethod(_this, buf, i);//parseCPInterface(_this, fp, i);
break;
case CONSTANT_NAME_AND_TYPE:
ptr = _parseCPNameAndType(_this, buf, i);
break;
case CONSTANT_METHOD_TYPE:
ptr = _parseCPMethodType(_this, buf, i);
break;
case CONSTANT_METHOD_HANDLE:
ptr = _parseCPMethodHandle(_this, buf, i);
break;
case CONSTANT_INVOKE_DYNAMIC:
ptr = _parseCPInvokeDynamic(_this, buf, i);
break;
default:
jvm_printf("\n!!!unknow constant item tag = %02x!!!\n\n", tag);
//fseek(fp, -1, SEEK_CUR);
break;
};
offset_end = buf->rp;
_this->constant_item_ptr[idx] = ptr;
}
return 0;
解析基本类型
这里以 UTF-8 String 常量为例:
/* parse UTF-8 String */
void *_parseCPString(JClass *_this, ByteBuf *buf, s32 index) {
//UTF-8 String 对象
ConstantUTF8 *ptr = jvm_calloc(sizeof(ConstantUTF8));
ptr->item.tag = CONSTANT_UTF8;
ptr->item.index = index;
//fread(short_tmp, 2, 1, fp);
//读取字符串的长度
Short2Char s2c;
s2c.c1 = (c8) bytebuf_read(buf);//short_tmp[0];
s2c.c0 = (c8) bytebuf_read(buf);//short_tmp[1];
ptr->string_size = s2c.s;
ptr->utfstr = utf8_create();
s32 i = 0;
//一个个读取字符拼接成串
for (; i < ptr->string_size; i++) {
u8 ch = (u8) bytebuf_read(buf);//0;
//fread(&ch, 1, 1, fp);
utf8_append_part_c(ptr->utfstr, &ch, 0, 1);
}
//塞到类中的常量池中
arraylist_push_back(_this->constantPool.utf8CP, ptr);
return ptr;
}
解析引用常量
这类都类似,只需要读到引用的 index 就可以了
以对字符串的引用为例:
void *_parseCPStringRef(JClass *_this, ByteBuf *buf, s32 index) {
ConstantStringRef *ptr = jvm_calloc(sizeof(ConstantStringRef));
ptr->item.tag = CONSTANT_STRING_REF;
ptr->item.index = index;
//fread(short_tmp, 2, 1, fp);
Short2Char s2c;
s2c.c1 = (u8) bytebuf_read(buf);//short_tmp[0];
s2c.c0 = (u8) bytebuf_read(buf);//short_tmp[1];
ptr->stringIndex = s2c.s;
arraylist_push_back(_this->constantPool.stringRef, ptr);
return ptr;
}
解析 Field
依然是根据数量依次解析
/* parse Field Pool */
s32 _parseFP(JClass *_this, ByteBuf *buf) {
FieldInfo *ptr = &(_this->fieldPool.field[_this->fieldPool.field_used]);
/* access flag */
//fread(short_tmp, 2, 1, fp);
Short2Char s2c;
s2c.c1 = (u8) bytebuf_read(buf);//short_tmp[0];
s2c.c0 = (u8) bytebuf_read(buf);//short_tmp[1];
ptr->access_flags = s2c.s;
/* name index */
......
/* descriptor index */
......
/* attributes count */
......
if (ptr->attributes_count > 0) {
ptr->attributes = (AttributeInfo *) jvm_calloc(sizeof(AttributeInfo) * ptr->attributes_count);
} else {
ptr->attributes = NULL;
}
/* parse attributes */
_parseAttr(ptr, buf);
_this->fieldPool.field_used++;
return 0;
}
解析 Method
几乎与解析 Field 一样,无需赘述
优化
优化,或者叫预加载
还记得上面的步骤读取了很多 index 么
这个优化就是将 index 直接变成对常量的直接引用以加快运行时的效率
/**
* 把各个索引转为直接地址引用,加快处理速度
* @param clazz class
*/
void _class_optimize(JClass *clazz) {
Utf8String *ustr = class_get_utf8_string(clazz,
class_get_constant_classref(clazz, clazz->cff.this_class)->stringIndex);
//根据类名 index 找到类名
clazz->name = utf8_create_copy(ustr);
// if (utf8_equals_c(clazz->name, "javax/mini/eio/socket/PrivateOutputStream")) {
// int debug = 1;
// }
//根据接口名 index 找到接口名
s32 i;
for (i = 0; i < clazz->interfacePool.clasz_used; i++) {
ConstantClassRef *ptr = &clazz->interfacePool.clasz[i];
ptr->name = class_get_utf8_string(clazz, class_get_constant_classref(clazz, ptr->stringIndex)->stringIndex);
}
//根据 Field index 找到 Field 名和 类型信息等
for (i = 0; i < clazz->fieldPool.field_used; i++) {
FieldInfo *ptr = &clazz->fieldPool.field[i];
ptr->name = class_get_utf8_string(clazz, ptr->name_index);
ptr->descriptor = class_get_utf8_string(clazz, ptr->descriptor_index);
ptr->datatype_idx = getDataTypeIndex(utf8_char_at(ptr->descriptor, 0));
ptr->isrefer = isDataReferByIndex(ptr->datatype_idx);
ptr->datatype_bytes = data_type_bytes[ptr->datatype_idx];
ptr->isvolatile = ptr->access_flags & ACC_VOLATILE;
//类成员加到 List
//for gc iterator fast
if (isDataReferByIndex(ptr->datatype_idx)) {
if (ptr->access_flags & ACC_STATIC) {
arraylist_push_back_unsafe(clazz->staticFieldPtrIndex, (__refer) (intptr_t) i);
} else {
arraylist_push_back_unsafe(clazz->insFieldPtrIndex, (__refer) (intptr_t) i);
}
}
}
//根据 方法 index 找到方法名 和 签名信息等
for (i = 0; i < clazz->methodPool.method_used; i++) {
MethodInfo *ptr = &clazz->methodPool.method[i];
ptr->name = class_get_utf8_string(clazz, ptr->name_index);
ptr->descriptor = class_get_utf8_string(clazz, ptr->descriptor_index);
ptr->_this_class = clazz;
if (!ptr->paraType) {//首次执行
// eg: (Ljava/lang/Object;IBLjava/lang/String;[[[ILjava/lang/Object;)Ljava/lang/String;Z
ptr->paraType = utf8_create();
//parse method description return slots
//解析方法参数
ptr->para_slots = parseMethodPara(ptr->descriptor, ptr->paraType);
ptr->para_count_with_this = ptr->paraType->length;
if (!(ptr->access_flags & ACC_STATIC)) {
ptr->para_slots++;//add this pointer
ptr->para_count_with_this++;
}
s32 pos = utf8_indexof_c(ptr->descriptor, ")") + 1;
ptr->returnType = utf8_create_part(ptr->descriptor, pos, ptr->descriptor->length - pos);
}
s32 j;
//转attribute为CdoeAttribute
for (j = 0; j < ptr->attributes_count; j++) {
if (utf8_equals_c(class_get_utf8_string(clazz, ptr->attributes[j].attribute_name_index), "Code") == 1) {
// if (utf8_equals_c(clazz->name, "espresso/syntaxtree/ExpressionNode") && utf8_equals_c(ptr->name, "evaluateExp")) {
// int debug = 1;
// }
CodeAttribute *ca = jvm_calloc(sizeof(CodeAttribute));
_convert_to_code_attribute(ca, &ptr->attributes[j], clazz);
jvm_free(ptr->attributes[j].info);//无用删除
ptr->attributes[j].info = NULL;
ptr->converted_code = ca;
}
}
}
for (i = 0; i < clazz->attributePool.attribute_used; i++) {
AttributeInfo *ptr = &clazz->attributePool.attribute[i];
Utf8String *name = class_get_utf8_string(clazz, ptr->attribute_name_index);
if (utf8_equals_c(name, "SourceFile")) {
Short2Char s2c;
s2c.c1 = ptr->info[0];
s2c.c0 = ptr->info[1];
clazz->source = class_get_utf8_string(clazz, s2c.s);
} else if (utf8_equals_c(name, "BootstrapMethods")) {
_convert_2_bootstrap_methods(ptr, clazz);
}
}
//解析类型引用
for (i = 0; i < clazz->constantPool.classRef->length; i++) {
ConstantClassRef *ccr = (ConstantClassRef *) arraylist_get_value(clazz->constantPool.classRef, i);
ccr->name = class_get_utf8_string(clazz, ccr->stringIndex);
}
//解析方法引用
for (i = 0; i < clazz->constantPool.methodRef->length; i++) {
ConstantMethodRef *cmr = (ConstantMethodRef *) arraylist_get_value(clazz->constantPool.methodRef, i);
cmr->nameAndType = class_get_constant_name_and_type(clazz, cmr->nameAndTypeIndex);
cmr->name = class_get_utf8_string(clazz, cmr->nameAndType->nameIndex);
cmr->descriptor = class_get_utf8_string(clazz, cmr->nameAndType->typeIndex);
cmr->clsName = class_get_constant_classref(clazz, cmr->classIndex)->name;
// if (utf8_equals_c(clazz->name, "java/lang/String")) {
// printf("%s,%s\n", utf8_cstr(cmr->name), utf8_cstr(cmr->clsName));
// int debug = 1;
// }
if (cmr->para_slots == -1) {
Utf8String *tmps = utf8_create();
cmr->para_slots = parseMethodPara(cmr->descriptor, tmps);
utf8_destory(tmps);
}
}
//解析接口方法
for (i = 0; i < clazz->constantPool.interfaceMethodRef->length; i++) {
ConstantMethodRef *cmr = (ConstantMethodRef *) arraylist_get_value(clazz->constantPool.methodRef, i);
cmr->nameAndType = class_get_constant_name_and_type(clazz, cmr->nameAndTypeIndex);
cmr->name = class_get_utf8_string(clazz, cmr->nameAndType->nameIndex);
cmr->descriptor = class_get_utf8_string(clazz, cmr->nameAndType->typeIndex);
cmr->clsName = class_get_constant_classref(clazz, cmr->classIndex)->name;
if (cmr->para_slots == -1) {
Utf8String *tmps = utf8_create();
cmr->para_slots = parseMethodPara(cmr->descriptor, tmps);
utf8_destory(tmps);
}
}
}
//解析方法参数列表
s32 parseMethodPara(Utf8String *methodType, Utf8String *out) {
s32 count = 0;
Utf8String *para = utf8_create_copy(methodType);
utf8_substring(para, utf8_indexof_c(para, "(") + 1, utf8_last_indexof_c(para, ")"));
//从后往前拆分方法参数,从栈中弹出放入本地变量
int i = 0;
while (para->length > 0) {
c8 ch = utf8_char_at(para, 0);
//各种参数类型
switch (ch) {
case 'S':
case 'C':
case 'B':
case 'I':
case 'F':
case 'Z':
utf8_substring(para, 1, para->length);
utf8_append_c(out, "4");
count++;
break;
case 'D':
case 'J': {
utf8_substring(para, 1, para->length);
utf8_append_c(out, "8");
count += 2;
break;
}
//引用类型
case 'L':
utf8_substring(para, utf8_indexof_c(para, ";") + 1, para->length);
utf8_append_c(out, "R");
count += 1;
break;
//数组类型
case '[':
while (utf8_char_at(para, 1) == '[') {
utf8_substring(para, 1, para->length);//去掉多维中的 [[[[LObject; 中的 [符
}
if (utf8_char_at(para, 1) == 'L') {
utf8_substring(para, utf8_indexof_c(para, ";") + 1, para->length);
} else {
utf8_substring(para, 2, para->length);
}
utf8_append_c(out, "R");
count += 1;
break;
}
i++;
}
utf8_destory(para);
return count;
}
加载完的准备工作
class.c ------ class_prepar()
- 加载父类并保存父类指针到本类中
- 计算类的成员变量所占内存空间,这里为两种成员变量分别计算
– 静态变量
– 实例变量 - 为静态变量分配内存空间,指针直接保存到 JClass 结构体,很明显静态变量生命周期和类一样
- 为实例变量生成模板,类的实例变量所需内存 = 父类实例变量内存 + 子类实例变量内存,并且父类在前
- 提前计算类成员的偏移量并预计算字段在实例内存中的偏移,提高执行速度
s32 class_prepar(JClass *clazz, Runtime *runtime) {
if (clazz->status >= CLASS_STATUS_PREPARING)return 0;
clazz->status = CLASS_STATUS_PREPARING;
// if (utf8_equals_c(clazz->name, "java/lang/NullPointerException")) {
// int debug = 1;
// }
//加载并保存父类指针
s32 superid = clazz->cff.super_class;
if (superid && !clazz->superclass) {
ConstantClassRef *ccf = class_get_constant_classref(clazz, superid);
if (ccf) {
Utf8String *clsName_u = class_get_utf8_string(clazz, ccf->stringIndex);
JClass *other = classes_load_get_without_clinit(clsName_u, runtime);
clazz->superclass = other;
} else {
jvm_printf("error get superclass , class: %s\n", utf8_cstr(clazz->name));
}
}
int i;
// if (utf8_equals_c(clazz->name, "espresso/parser/JavaParser")) {
// int debug = 1;
// }
//准备成员变量所需要的空间,其实也是类的对象实例化所需要的内存空间
FieldInfo *f = clazz->fieldPool.field;
//计算不同种类变量长度
s32 static_len = 0;
s32 instance_len = 0;
for (i = 0; i < clazz->fieldPool.field_used; i++) {
s32 width = data_type_bytes[f[i].datatype_idx];
if (f[i].access_flags & ACC_STATIC) {//静态变量
f[i].offset = static_len;
static_len += width;
} else {//实例变量
f[i].offset = instance_len;
instance_len += width;
}
f[i]._this_class = clazz;
}
//静态变量分配
clazz->field_static_len = static_len;
//分配静态变量的内存空间
clazz->field_static = jvm_calloc(clazz->field_static_len);
//生成实例变量模板
if (clazz->superclass) {
clazz->field_instance_start = clazz->superclass->field_instance_len;
clazz->field_instance_len = clazz->field_instance_start + instance_len;
//实例变量区前面是继承的父类变量,后面是自己的变量
//memcpy((clazz->field_instance_template), (superclass->field_instance_template), clazz->field_instance_start);
} else {
clazz->field_instance_start = 0;
//实例变量区前面是继承的父类变量,后面是自己的变量
clazz->field_instance_len = clazz->field_instance_start + instance_len;
}
//提前计算类成员的偏移量,提高执行速度
for (i = 0; i < clazz->fieldPool.field_used; i++) {
FieldInfo *fi = &clazz->fieldPool.field[i];
fi->offset_instance = fi->_this_class->field_instance_start + fi->offset;
}
//预计算字段在实例内存中的偏移,节约运行时时间
if (utf8_equals_c(clazz->name, STR_CLASS_JAVA_LANG_CLASS)) {
FieldInfo *fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_CLASS, STR_FIELD_CLASSHANDLE, "J", runtime);
jvm_runtime_cache.class_classHandle = fi;
} else if (utf8_equals_c(clazz->name, STR_CLASS_JAVA_LANG_STRING)) {
FieldInfo *fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STRING, STR_FIELD_COUNT, "I", runtime);
jvm_runtime_cache.string_count = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STRING, STR_FIELD_OFFSET, "I", runtime);
jvm_runtime_cache.string_offset = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STRING, STR_FIELD_VALUE, "[C", runtime);
jvm_runtime_cache.string_value = fi;
} else if (utf8_equals_c(clazz->name, STR_CLASS_JAVA_LANG_STRINGBUILDER)) {
FieldInfo *fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STRINGBUILDER, STR_FIELD_COUNT, "I", runtime);
jvm_runtime_cache.stringbuilder_count = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STRINGBUILDER, STR_FIELD_VALUE, "[C", runtime);
jvm_runtime_cache.stringbuilder_value = fi;
} else if (utf8_equals_c(clazz->name, STR_CLASS_JAVA_LANG_THREAD)) {
FieldInfo *fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_THREAD, STR_FIELD_NAME, "[C", runtime);
jvm_runtime_cache.thread_name = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_THREAD, STR_FIELD_STACKFRAME, "J", runtime);
jvm_runtime_cache.thread_stackFrame = fi;
} else if (utf8_equals_c(clazz->name, STR_CLASS_JAVA_LANG_STACKTRACE)) {
FieldInfo *fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STACKTRACE, "declaringClass", STR_INS_JAVA_LANG_STRING, runtime);
jvm_runtime_cache.stacktrace_declaringClass = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STACKTRACE, "methodName", STR_INS_JAVA_LANG_STRING, runtime);
jvm_runtime_cache.stacktrace_methodName = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STACKTRACE, "fileName", STR_INS_JAVA_LANG_STRING, runtime);
jvm_runtime_cache.stacktrace_fileName = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STACKTRACE, "lineNumber", "I", runtime);
jvm_runtime_cache.stacktrace_lineNumber = fi;
fi = find_fieldInfo_by_name_c(STR_CLASS_JAVA_LANG_STACKTRACE, "parent", STR_INS_JAVA_LANG_STACKTRACEELEMENT, runtime);
jvm_runtime_cache.stacktrace_parent = fi;
}
// jvm_printf("prepared: %s\n", utf8_cstr(clazz->name));
clazz->status = CLASS_STATUS_PREPARED;
return 0;
}
类初始化
class.c ------ class_clinit()
- 把一些索引引用,转为内存对象引用,以此加快字节码执行速度
– 把 ConstantMethodRef.index 指向具体的 MethodInfo ,可能在本类,可能在父类
– 把 ConstantFieldRef.index 指向具体的 FieldInfo 内存 - 单独保存 finalize() 方法引用,以备 GC 使用
- 初始化类常量中的 String 对象
- 优先初始化基类
- 调用类初始化方法,即静态块
void class_clinit(JClass *clazz, Runtime *runtime) {
garbage_thread_lock();
runtime->threadInfo->no_pause++;
if (clazz->status < CLASS_STATUS_PREPARED) {
class_prepar(clazz, runtime);
}
if (clazz->status < CLASS_STATUS_CLINITING) {
clazz->status = CLASS_STATUS_CLINITING;
s32 i, len;
/**
* 把一些索引引用,转为内存对象引用,以此加快字节码执行速度
* 把ConstantMethodRef.index 指向具体的 MethodInfo ,可能在本类,可能在父类
* 把ConstantFieldRef.index 指向具体的 FieldInfo 内存
* @param clazz
*/
for (i = 0; i < clazz->constantPool.methodRef->length; i++) {
ConstantMethodRef *cmr = (ConstantMethodRef *) arraylist_get_value(clazz->constantPool.methodRef, i);
cmr->methodInfo = find_methodInfo_by_methodref(clazz, cmr->item.index, runtime);
cmr->virtual_methods = pairlist_create(0);
//jvm_printf("%s.%s %llx\n", utf8_cstr(clazz->name), utf8_cstr(cmr->name), (s64) (intptr_t) cmr->virtual_methods);
}
for (i = 0; i < clazz->constantPool.interfaceMethodRef->length; i++) {
ConstantMethodRef *cmr = (ConstantMethodRef *) arraylist_get_value(clazz->constantPool.interfaceMethodRef, i);
cmr->methodInfo = find_methodInfo_by_methodref(clazz, cmr->item.index, runtime);
cmr->virtual_methods = pairlist_create(0);
}
for (i = 0; i < clazz->constantPool.fieldRef->length; i++) {
ConstantFieldRef *cfr = (ConstantFieldRef *) arraylist_get_value(clazz->constantPool.fieldRef, i);
FieldInfo *fi = find_fieldInfo_by_fieldref(clazz, cfr->item.index, runtime);
cfr->fieldInfo = fi;
if (!fi) {
jvm_printf("field not found %s.%s \n", utf8_cstr(class_get_constant_classref(clazz, cfr->classIndex)->name), utf8_cstr(class_get_constant_utf8(clazz, class_get_constant_name_and_type(clazz, cfr->nameAndTypeIndex)->nameIndex)->utfstr));
fi = find_fieldInfo_by_fieldref(clazz, cfr->item.index, runtime);
}
if (fi->_this_class->status < CLASS_STATUS_CLINITED) {
class_clinit(fi->_this_class, runtime);
}
}
//发现 finalize()
//find finalize method, but not process java.lang.Object.finalize()
clazz->finalizeMethod = find_methodInfo_by_name_c(utf8_cstr(clazz->name), STR_METHOD_FINALIZE, "()V", runtime);
if (clazz->finalizeMethod && utf8_equals_c(clazz->finalizeMethod->_this_class->name, STR_CLASS_JAVA_LANG_OBJECT)) {
clazz->finalizeMethod = NULL;
} else {
int debug = 1;
}
//初始化类常量中的 String 对象
// init javastring
ArrayList *strlist = clazz->constantPool.stringRef;
for (i = 0, len = strlist->length; i < len; i++) {
ConstantStringRef *strRef = arraylist_get_value_unsafe(strlist, i);
ConstantUTF8 *cutf = class_get_constant_utf8(clazz, strRef->stringIndex);
Instance *jstr = hashtable_get(sys_classloader->table_jstring_const, cutf->utfstr);
if (!jstr) {
jstr = jstring_create(cutf->utfstr, runtime);
hashtable_put(sys_classloader->table_jstring_const, cutf->utfstr, jstr);
gc_refer_hold(jstr);
}
cutf->jstr = jstr;
}
//优先初始化基类
JClass *superclass = getSuperClass(clazz);
if (superclass && superclass->status < CLASS_STATUS_CLINITED) {
class_clinit(superclass, runtime);
}
//调用类初始化方法,即静态块
MethodPool *p = &(clazz->methodPool);
for (i = 0; i < p->method_used; i++) {
//jvm_printf("%s,%s\n", utf8_cstr(p->methodRef[i].name), utf8_cstr(p->methodRef[i].descriptor));
if (utf8_equals_c(p->method[i].name, STR_METHOD_CLINIT)) {
#if _JVM_DEBUG_BYTECODE_DETAIL > 5
jvm_printf(" <clinit> :%s\n", utf8_cstr(clazz->name));
#endif
s32 ret = execute_method_impl(&(p->method[i]), runtime, clazz);
if (ret != RUNTIME_STATUS_NORMAL) {
print_exception(runtime);
}
break;
}
}
//MUST load all relative classes, because instanceof maybe match ins to parent's parent, but it not in memory
// for (i = 0; i < clazz->constantPool.classRef->length; i++) {
// ConstantClassRef *ccr = (ConstantClassRef *) arraylist_get_value(clazz->constantPool.classRef, i);
// classes_load_get(ccr->name, runtime);
// }
clazz->status = CLASS_STATUS_CLINITED;
}
runtime->threadInfo->no_pause--;
garbage_thread_unlock();
}