虚拟机类加载机制

类加载的时机

类从被加载到虚拟机内存开始,到卸载出内存开始,生命周期包括七个阶段

类加载工程

其中”验证“,”准备“,”解析“三个部分统称为连接(Linking)

对于初始化,虚拟机是严格规定了有且只有四种情况必须立即对类进行”初始化“

  • 1.遇到 new, getstatic, putstatic, 或invokestatic 这4条字节码指令,如果没有初始化则先初始化。这4条指令对应的java代码场景是:

    • 使用 new 关键字实例化对象的时候
    • 读取或设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的静态字段除外)
    • 调用一个类的静态方法的时候
  • 2.使用 java.long.reflect包的方法对类进行反射调用,如果类没有初始化则先初始化

  • 3.当初始化类时,如发现父类没有初始化,则先初始化父类

  • 4.当虚拟机启动,用户需要指定一个执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

这四种场景称为对一个类进行主动引用。除此之外所有引用称为被动引用,都不会触发初始化。
下面的代码是被动引用的例子

package org.fenixsoft.classloading;

/**
* 被动使用类字段演示一:
* 通过子类引用父类的静态字段,不会导致子类初始化
**/
public class SuperClass {

    static {
        System.out.println("SuperClass init!");//会输出
    }

    public static int value = 123;
}

public class SubClass extends SuperClass {

    static {
        System.out.println("SubClass init!");//不会输出
    }
}

/**
* 非主动使用类字段演示
**/
public class NotInitialization {

    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }

}

上面代码只触发父类初始化,子类不会初始化。对于静态字段,只有直接定义这个字段的类才会被初始化。

package org.fenixsoft.classloading;

/**
* 被动使用类字段演示二:
* 通过数组定义来引用类,不会触发此类的初始化
**/
public class NotInitialization {

    public static void main(String[] args) {
        SuperClass[] sca = new SuperClass[10];
    }

}

上述代码没有输出"SuperClass init!".这段代码触发了名为”[Lorg.fenixsoft.classloading.SuperClass“的类的初始化,这不是合法的类名称,是由java虚拟机自动生成的,直接继承与Object的子类,创建动作由字节码指令newarray触发。

这个类代表了一个元素类型为”org.fenixsoft.classloading.SuperClass"的一维数组。

package org.fenixsoft.classloading;

/**
* 被动使用类字段演示三:
* 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定   义常量的类的初始化。
**/
public class ConstClass {

    static {
        System.out.println("ConstClass init!");//不会输出
    }

    public static final String HELLOWORLD = "hello world";
}

/**
* 非主动使用类字段演示
**/
public class NotInitialization {

    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);
    }
}

上述代码没有输出”ConstClass init!“因为在编译阶段将此常量的值"hello world"存储到NotInitialization类的常量池中了。对常量ConstClass.HELLOWORLD的引用转化成NotInitialization类对自身常量池的引用了。也就是说NotInitialization的class文件之中没有ConstClass类的符号引用入口,这两个类在编译成class之后就不存在任何联系了。

接口与类加载的过程有不同,主要区别在于:

  • 当一个类在初始化时,要求其父类进行初始化。但是当一个接口在初始化时,并不要求其父接口全部都完成初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。

类加载器

类与类加载器

比较两个类是否"相等",只有在这两个类是由同一个类加载器加载的前提下才有意义。否则即使两个类是来源同一个class文件,只要加载它们的类加载器不同,那两个类就必定不相等。

这里的”相等“包括代表类的class对象的equals()方法,isAssignableFrom(), isInstance()方法的返回结果,也包括了使用instanceof关键字做对象所属关系判定等情况。

/**
* 类加载器与instanceof关键字演示
* 
* @author zzm
*/
public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {

        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws   ClassNotFoundException {
            try {
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream is = getClass().getResourceAsStream(fileName);
                if (is == null) {
                    return super.loadClass(name);
                }
                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                throw new ClassNotFoundException(name);
            }
        }
    };

    Object obj = myLoader.loadClass("org.fenixsoft.classloading.ClassLoaderTest").newInstance();

    System.out.println(obj.getClass());
    System.out.println(obj instanceof org.fenixsoft.classloading.ClassLoaderTest);
}
}

运行结果:
class org.fenixsoft.classloading.ClassLoaderTest
false

虚拟机中存在两个ClassLoaderTest类,类加载器不同。

双亲委派模型

  • 启动类加载器(Bootstrap ClassLoader): C++语言实现,是虚拟机自身的一部分。负责将存放在<JAVA_HOME>\lib目录中的类库加载到虚拟机内存中。启动器类加载器无法被java程序直接引用。

  • 扩展类加载器(Extension ClassLoader): 负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可直接使用扩展类加载器。

  • 应用程序类加载器(Application ClassLoader):由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

  • 自定义类加载器

模型
  • 流程是当一个classlaoder尝试加载一个类时,它会先抛给父加载器,一层层的往上抛,抛到最上层启动类加载器。如果启动类加载器无法加载,这时会自上而下的抛给其他类加载器,直到可以加载为止

双亲委派模型的实现

protected synchronized Class<?> loadClass(String name, boolean resolve)     throws ClassNotFoundException {
    //首先,检查请求的类是否已经被加载过了
    Class c = findLoadedClass(name);
    if (c == null){
        try{
            if (parent != null){
                c = parent.loadClass(name,false);
            }else {
                c = findBootstrapClassOrNull(name);
            }
        }catch(ClassNotFoundException e){
            //如果父类加载器抛出异常
            //说明父类加载器无法完成加载请求
        }
        if(c == null){
            //在父类加载器无法加载的时候
            //再调用本身的findClass方法来进行类加载
            c = findClass(name);
        }
    }
    if (resolve){
        resolveClass(c);
    }
    return c ; 
}

参考:

《深入理解java虚拟机》周志明 著

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容