JVM 之 类加载

Java 虚拟机将字节流转化为 Java 类的过程,可以分为三大步骤。

1、加载

查找字节流,并且据此创建类的过程。

将类的.class 文件中的二进制数据读到内存中,将其放在运行时数据区域的方法区里,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品位于堆中的 Class 对象,Class 对象封装了类在方法区内的数据结构。并向 Java 程序员提供了访问方法区内的数据结构的接口。


JVM 之 类加载_第1张图片

1 ) 通过一个类的全限定名来获取其定义的二进制字节流。
2 )将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3 ) 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

2、链接(验证、准备、解析)

将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。分为验证、准备、解析三个阶段,解析是非必须的。

  • 验证: 保证被加载的类满足 Java 虚拟机的约束条件
  • 准备:类中的静态变量分配内存并初始化默认值(不是真正的初始值)。
  • 解析: 把类中的符号引用变为直接引用。

3、初始化

为类的静态变量赋值(正确的初始值)

准备:
为被加载类的静态字段分配内存。Java 代码中对静态字段的初始化。还会再此阶段构造其他跟类层次相关的结构数据。比如用来实现虚方法的动态绑定的方法表。在 class 文件被加载到 Java 虚拟机之前,这个类别说 无法知道其他类的地址,就是自己的方法,字段的地址都不知道,所以当我们用这些成员的时候,Java 编译器会生成一个符号引用( 这就是符号引用的出处,和之前文章的知识连接起来了)在运行期间,这些符号引用定位到具体目标上。

解析:
就是将这些符号引用解析成为实际的引用。如果一个符号引用指向一个未被加载的类或者未被加载的字段,那么解析将触发这个类的加载。没被加载的赶紧去加载。

JVM 之 类加载_第2张图片

从这里来看类也是有生命周期的,那么类什么时候卸载呢?

1、该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。

2、加载该类的ClassLoader已经被回收。

3、该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了。

不过说实话让 class 卸载还是挺难的,只要正规来一般不会卸载的。特别是 Android 这种卸载除了自定义类加载外别的地方貌似挺难实现的。

最后 说一下 我自己的一个误区,导致很多地方都想不明白。


  • 对事物的抽象

  • 实例

对类的实例化,类是描述 ,实例化相当于开辟一块内存,创造出各种属性,也可以创造多个实例区域,对象不同而已。

  • Class

这是一个实实在在存在的类,它是一个类,类,都是泪啊。就栽倒在这里了。

在 java.lang 包下 Class 的实例就是 Class 的实例,Class 是表示类的类。
java提供了下面几种获取到类的Class对象的方法:

1、 利用对象实例调用getClass()方法获取该对象的Class实例;
2、 使用Class类的静态方法forName("包名+类名"),用类的名字获取一个Class实例
3、运用 类名.class 的方式来获取Class实例;

当一个类加载的时候,会产生一个 Class 对象(Class 类 的对象),用来表示这个类,该类的所有实例都共同拥有这个 Class 对象,而且是唯一的。

参考:
https://www.jianshu.com/p/61fe6a67defa
https://blog.csdn.net/wodeyuer125/article/details/39028103
https://blog.csdn.net/evilcry2012/article/details/78983175

你可能感兴趣的:(JVM 之 类加载)