java 虚拟机类型的卸载_《深入理解Java虚拟机》:类加载和初始化(二)

《深入理解Java虚拟机》:类加载和初始化(二)

在去年看《深入理解Java虚拟机》的时候,写过一篇关于类加载和初始化的博客,最近又在看这一块的知识,发现还是有很多东西没有理解好。借助于网上的一些博客,学习了一点新的知识,并整理如下。

1、类加载过程

类加载过程由如下几个阶段构成:装载、链接、初始化。其中链接包括:验证、准备和解析三个阶段。

1、装载:查找并加载类的二进制数据

2、链接:

验证:确保被加载类的正确性

准备:为类的静态属性分配内存并初始化为默认值。

解析:把类中的符号引用转换为直接引用

3、初始化:为类的静态变量赋予正确的初始值。

那为什么还要有验证这一步骤呢?

首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。

准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的.

如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

2.类的初始化

类什么时候才被初始化:

1)创建类的实例,也就是new一个对象

2)访问某个类或接口的静态变量,或者对该静态变量赋值

3)调用类的静态方法

4)反射(Class.forName(“com.lyj.load”))

5)初始化一个类的子类(会首先初始化子类的父类),注:子类初始化问题:满足主动调用,即父类访问子类中的静态变量、方法,子类才会初始化;否则仅父类初始化。具体看这篇博客:http://blog.csdn.net/u010412719/article/details/47059439

6)JVM启动时标明的启动类,即文件名和类名相同的那个类

只有这6中情况才会导致类的类的初始化。

类的初始化步骤

1)如果这个类还没有被加载和链接,那先进行加载和链接

2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)

3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。

3.加载器

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:(七牛云存储好像出bug了,访问不了,因此就没有图片)

Bootstrap classLoader:加载核心库文件

Extension classLoader:加载扩展库文件

App classLoader:加载classpath路径下的jar包,我们写的java类,一般都是由它加载,除非你自己制定个人的类加载器。

loadClass方法的源码如下:

public Class> loadClass(String name) throws ClassNotFoundException {

return loadClass(name, false);

}

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// First, check if the class has already been loaded

Class> c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

/* 如果有父类加载器,则交给父类加载器去加载。 如果没有父类加载器,则说明此加载器为extension classLoader,直接交给Bootstrap classLoader加载器去加载。 */

if (parent != null) {

c = parent.loadClass(name, false);//交给父类来加载

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

/* 如果父类加载器没有加载成功,则自己加载。 */

if (c == null) {

// If still not found, then invoke findClass in order

// to find the class.

long t1 = System.nanoTime();

c = findClass(name);

// this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

源码思想思路如下:

假设加载类A,加载过程如下

1、首先检查当前加载器是否加载了类 A,如果没有,则进行 2,否则进行 7

2、检查当前加载器是否有父加载器,如果有,则进行 3, 否则说明当前加载器为extension classLoader,因此进行 4.

3、当类A委托给父加载器进行加载(父类加载器加载的过程和这个过程类似,也是先检查类A是否已经加载,

如果没有,检查是否有父类加载器,如果有,则进一步委托给父加载器),如果加载成功,则 进行 7,否则进行 5.

4、将类A委托给Bootstrap classLoader进行加载,如果加载成功,则进行 7,否则进行 5.

5、检查类A是否已经加载成功,如果没有,则进行 6,否则进行 7.

6、当前加载前将自己亲自加载类 A .

7、退出

总结:加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类,即从Bootstrap classLoader到custom classLoader逐层尝试加载此类。

类的卸载

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值