Class 字节码格式
总体结构
plaintext
ClassFile {
u4 magic; // 魔数:0xCAFEBABE(4字节)
u2 minor_version; // 次版本(2字节)
u2 major_version; // 主版本(2字节)
u2 constant_pool_count; // 常量池长度(2字节)
cp_info constant_pool[]; // 常量池(变长)
u2 access_flags; // 访问标志(public/final等,2字节)
u2 this_class; // 当前类索引(2字节)
u2 super_class; // 父类索引(2字节)
u2 interfaces_count;// 接口数(2字节)
u2 interfaces[]; // 接口索引数组
u2 fields_count; // 字段数(2字节)
field_info fields[]; // 字段表(变长)
u2 methods_count; // 方法数(2字节)
method_info methods[]; // 方法表(变长,含字节码)
u2 attributes_count; // 属性数(2字节)
attribute_info attributes[]; // 属性表(变长)
}
magic number
每个 Class 文件开头的 4 字节整数 -> 0xcafebabe
版本
记录编译器版本, 判断当前的 jvm 是否能够兼容该 class 文件
常量池计数器
记录常量池中有多少常量(从 1 开始计数)
字节码指令
程序实际要运行的指令
类加载机制
加载过程
装载
将 class 文件加载到内存中,并创建一个 Class 对象
链接
验证
确认加载的字节码是否符合规范
准备
为静态变量分配内存,并赋予默认值
解析
将类、接口、方法的符号引用转变为直接引用
初始化
为静态变量赋予正确的初始值(static final 是例外,常量值是在链接的准备阶段进行赋值)
类加载器
启动类加载器
没有父加载器
加载 Java 核心库(JAVA_HOME/jre/lib/rt.jar)
扩展类加载器
继承自 ClassLoader 类
父加载器为启动类加载器
加载 jre/lib/ext 目录下的类库
应用程序类加载器
继承自 ClassLoader 类
父加载器为扩展类加载器
加载环境变量 classpath 下的类库
双亲委派模型
当需要加载某些类时,类加载器会把请求先转给父类加载器,即最顶层的类加载器先加载,加载不到就转给子类加载器,如果最终到达最底层类加载器依旧没有找到,跑出 ClassNotFoundException
如何破坏双亲委派模型
- 重写自定义类加载器的 loadClass 方法
- 使用线程上下文类加载器,通过Thread.currentThread().getContextClassLoader() 拿到应用类加载器,反向加载类,绕过委派规则
- SPI 机制