目录

JVM类加载机制

目录

JVM类加载机制

Java编译之后生成的class文件有点类似C中的结构体,内部只有无符号数和表,其二进制文件是按一个个字节排列的,前八个字节为魔数;CAFEBABE,接着是版本号和次版本号,JDK只能运行低于自身版本的class文件,接着是常量池相关的数据,常量的个数是由1开始而不是0。接着是常量池中的数据,都是以_info结尾的。

常量表的内容举例

https://i-blog.csdnimg.cn/direct/8fb7add1e4f444949abaaa518102e1d6.png

类的构造方法在Class文件中的标识

https://i-blog.csdnimg.cn/direct/69b65212c4304c158a75a06e4be88f9e.png

常量池之后是该类的访问标志

https://i-blog.csdnimg.cn/direct/b8dadfc52ee94cfdb00ccbe275b9af72.png

会将该类的所有标志值进行按位或运算得到对应的值,作为相应的标志,接下来就是该类的类信息,父类与接口信息,字段与方法信息。详细需要了解可以查书。

字节码指令

由于Java指令主要是针对操作数栈,因此通常不带参数。

https://i-blog.csdnimg.cn/direct/40cca244aacd4cd2a64255bdbc91458d.png

ASM字节码编程

可以根据字节码文件的格式,利用ASM框架进行字节码编程从而跳过编译。

创建流程

https://i-blog.csdnimg.cn/direct/f652e71cecff4a9ca46d74b52017753b.png

https://i-blog.csdnimg.cn/direct/765422592b4149fe86fa08566db7c28d.png https://i-blog.csdnimg.cn/direct/918a602257db4e59a2bc0a127d0e863e.png

此时没有无参构造方法需要为我们自己添加

https://i-blog.csdnimg.cn/direct/40add35b82f14856a128bf39ead462d8.png 但是这样是不符合规范的,还需要调用父类的无参构造方法。

https://i-blog.csdnimg.cn/direct/72b797252d7c4d5b8cb93a2840c289af.png

这样才能拼凑好一个Class类文件

类加载流程

类的字节码文件并不是一下子就加载进内存的,而是在以下的情况会加载进内存

https://i-blog.csdnimg.cn/direct/d7919df943e64756a4b9b2e0a43848b1.png

如果用到另一个类的字段,就会自动将另一个类加载进内存。

但是如果使用的是静态不可变的字段,那么该字段所属的类不会被加载,因为使用的是常量池中的内容直接替换Test.str,是编译器进行的优化,因为final字段导致其不会变化,对象数组的定义不会触发类的加载。

类的生命周期

总共有七个 https://i-blog.csdnimg.cn/direct/1df027e71ad949a793c77c887a013d9c.png

加载就是要获取类的二进制比特流,可以从磁盘甚至网络中获取。、

验证就是要确认字节码文件正常不会危害虚拟机,例如检查版本或者是魔数是否为CADEBABE

准备就是为类变量分配内存,并为变量分配初始值。

解析就是将常量池中的符号引用替换为直接引用,此时所有变量指向对应内存的对象。

此时链接过程就完成,下一步就是进行真正的初始化,例如clinit就是这个时候进行的。

类加载器

Java提供了,同时也可以自己定义,对于每一个类,即使字节码文件相同,不同加载器加载结果不同,二者联合唯一确定一个类,默认都是使用JDK自带类加载器加载。

https://i-blog.csdnimg.cn/direct/5528e45d6d2f447b88cf640b99f4b6cf.png