引用了
前言
1.最近在学习关于虚拟机的知识,整理一份比较详细的class文件结构。
2.翻译class文件就可以称为解释器
3.你可以使用javap -v Hello.class 命令去查看class类文件结构
4.可以从你的目标目录和安装目录的rt.jar中读取基本够用的class文件
读取字节码
private fun getClassBit(goal: String): ByteArrayBean {
var result = getFromJar("/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/rt.jar", goal)
if (result == null) {
result = getFromFile("/Users/haha/Downloads/", goal)
}
return ByteArrayBean(result, 0)
}
private fun getFromJar(path: String, goal: String): ByteArray? {
val zipInputStream = ZipInputStream(FileInputStream(path))
val zipFile = ZipFile(path)
var zipEntry: ZipEntry?
while (true) {
zipEntry = zipInputStream.nextEntry
if (zipEntry == null) {
break
}
if (zipEntry.name == goal.plus(".class")) {
return zipFile?.getInputStream(zipEntry).readBytes()
}
}
return null
}
private fun getFromFile(path: String, goal: String): ByteArray {
val file = File("$path$goal.class")
return file.readBytes()
}
Class类文件结构
名称 |
变量 |
长度/Byte |
魔数 |
magic |
4 |
次版本号 |
minor_version |
2 |
主版本号 |
major version |
2 |
常量数 |
constant_num |
2 |
常量池 |
constant_pool |
常量*(constant_num - 1) |
访问标志 |
access_flags |
2 |
类索引 |
this_class |
2 |
父类索引 |
super_class |
2 |
接口数量 |
interface_num |
2 |
接口池 |
interface_pool |
接口*(interface_num) |
变量数量 |
field_num |
2 |
变量池 |
field_pool |
变量*(field_num) |
方法数量 |
method_num |
2 |
方法池 |
method_pool |
方法*(method_num) |
附加属性数量 |
attribute_num |
2 |
附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
常量结构
名称 |
变量 |
长度/byte |
类型 |
type |
1 |
内容 |
content |
不固定,由下表决定 |
类型值 |
名称 |
格式 |
长度/bytes |
说明 |
1 |
UTF8 |
length |
2 |
长度 |
|
|
Text |
length |
内容 |
3 |
Int |
|
4 |
值 |
4 |
Float |
|
4 |
值 |
5 |
Long |
|
8 |
值 |
6 |
Double |
|
8 |
值 |
7 |
Class |
index |
2 |
指向index位置的utf8常量 |
8 |
String |
index |
2 |
指向index位置的utf8常量 |
9 |
Fieldref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
10 |
Methodref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
11 |
Interface Methodref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
12 |
NameAndType |
name index |
2 |
指向index位置的utf8常量 |
|
|
type index |
2 |
指向index位置的utf8常量 |
备注:当常量池出现long时,constant_pool应该跳过一位,例如constant_pool[16]=1280977330748,下一位应该给constant_pool[18]赋值,原因未知,double未测试,测试class文件为String.class
接口结构
名称 |
长度 |
Interface Classref Index |
2 |
备注:接口池存的很简单,只是指向常量池中接口类的索引
变量结构
名称 |
变量 |
长度/bytes |
访问标志 |
access_flags |
2 |
变量名索引 |
name_index |
2 |
变量类型索引 |
descriptor_index |
2 |
附加属性数量 |
attribute_num |
2 |
附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
方法结构
名称 |
变量 |
长度/bytes |
访问标志 |
access_flags |
2 |
方法名索引 |
name_index |
2 |
方法类型索引 |
descriptor_index |
2 |
附加属性数量 |
attribute_num |
2 |
附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
附加属性结构
名称 |
变量 |
长度/bytes |
属性名索引 |
name_index |
2 |
长度 |
length |
4 |
内容 |
attribute_pool |
length |
根据name_index指向的属性决定,属性bean可能为不同格式,不同长度
方法结构-附加属性-Code属性(属于附加属性,包含附加属性)
名称 |
变量 |
长度/bytes |
最大栈深度 |
stack_length |
2 |
局部变量表大小 |
localproperty_num |
2 |
指令长度 |
order_length |
4 |
指令集 |
order_array |
order_length |
异常数量 |
exception_num |
2 |
异常池 |
exception_pool |
异常属性*exception_num |
附加属性数量 |
attribute_num |
2 |
附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
方法结构-附加属性-Code属性-异常属性
名称 |
变量 |
长度/bytes |
起始指令位置 |
from |
2 |
结束指令位置 |
to |
2 |
目标指令位置 |
target |
2 |
异常类型 |
type |
2 |
已知附加属性
属性名 |
class |
field |
method |
method-code |
Signature |
* |
* |
* |
|
Code |
|
|
* |
|
LineNumberTable |
|
|
|
* |
LocalVariableTable |
|
|
|
* |
StackMapTable |
|
|
|
* |
Deprecated |
|
|
* |
|
RuntimeVisibleAnnotations |
|
|
* |
|
Exceptions |
|
|
* |
|
SourceFile |
* |
|
|
|
InnerClasses |
* |
|
|
|
类访问标志符
方法访问标志符