JVM类加载机制

1. 简介

Java代码编译成字节码后,字节码需要通过虚拟机的加载才能运行

2. 类加载过程

类加载的全过程分为5个阶段,其中验证、准备、解析属于连接阶段

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

加载

加载是类加载过程的一个阶段,此阶段可以分为三步

  1. 通过一个类的全限定名(如java.lang.Object)来获取此类的二进制流
  2. 将这个字节流所代表的静态数据结构方法区运行时的数据结构
  3. 在内存中生成代表这个类的Class对象(Java虚拟机规范并没有明确规定这个对象在Java堆里,但是对于HotSpot虚拟机而言,Class对象存在于方法区中)

这个阶段是由类加载器(ClassLoader)来完成

验证

验证是连接阶段的第一步,目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求。包括四个方面的验证:

  • 文件格式验证
  • 元数据验证
  • 字节码验证
  • 符号引用验证

准备

准备阶段时正式为类变量分配内存并设置类变量初始值的阶段,初始值一般是零值,不一般的情况就是类变量用 final修饰初始值是绑定的常量值。

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

初始化

  • 初始化阶段是执行类构造器<clinit>()方法的过程,即静态语句块。
  • 虚拟机会保证父类的<clinit>()方法先执行。而如果初始化的是接口,并不需要先执行父接口的<clinit>()方法,只有在使用父接口定义的变量时才会执行。
  • 虚拟机会保证执行<clinit>()方法时会正确加锁同步。

3. 类加载器

类加载的加载阶段是由类加载器来完成的。即使是同一个类,由不同的类加载器来加载,两个类也是不相等的,体现在:

  • Class对象的equals()方法
  • Class对象的isAssignableFrom方法
  • Class对象的isInstance()方法
  • 使用instanceof关键字判断对象所属的类

类加载器的分类

对于虚拟机来说只存在两种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)
    • 使用C++实现,属于虚拟机自身的一部分。
    • 这个类负责加载<JAVA_HOME>\lib目录下,或者被-Xbootclasspath参数指定的路径,并且能够被虚拟机识别的类
    • 启动类加载器无法直接被Java程序引用
  2. 其他类加载器。使用Java实现,独立于虚拟机外部。并且继承与抽象类java.lang.ClassLoader
    • 扩展类加载器(Extension ClassLoader)
      • 这个类加载器是由sun.misc.Launcher$ExtClassLoader实现
      • 负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量指定的路径的所有类库
      • 扩展类加载器可以直接使用
    • 应用程序类加载器(Application ClassLoader)
      • 这个类加载器是由sun.misc.Launcher$AppClassLoader实现
      • 负责加载用户类路径(ClassPath)上的类库
      • 应用程序类加载器可以直接使用,同时这个类加载器也是ClassLoader类的getSystemClassLoader()静态方法的返回值

类加载器的层次关系(双亲委派模型)

双亲委派模型

下图是类加载器之间的层次关系,也称为双亲委派模型(Parents Delegation Model)。可以看出除了启动类加载器之外,其他类加载器都有自己的父加载器,但这里的层次关系是以组合来实现,而不是继承(extends)。

双亲委派模型

工作方式

双亲委派模型的工作方式是:当一个类加载器收到加载类的请求时,它会先把这个加载请求委托给父类加载器,如果父类加载器不能完成加载(如在自己的搜索路径下找不到该类),这时再由自己来完成加载这个类。通过这个方法,加载请求会委托到启动加载器,如果启动类加载器完成不了再依次向下的类加载器加载。

作用

双亲委派模型的作用是所有的加载请求都最先由顶层的类加载器去完成,这样可以保证加载出来的类是同一个,不会混乱。
打个比方,我们自己实现了两个类加载器,而这个两个类加载器都不是按照双亲委派模型去实现的,而是自己去对应目录下加载类。现在我们让两个类加载器加载去加载java.lang.Object类,加载出来的两个类必定是不同的(不同的类加载器加载出来的类是不同的)。

自定义类加载器

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 {
                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;
    }
}

上面的代码是抽象类ClassLoaderloadClass方法,通过类的全限定名来加载类,其中loadClass(String name, boolean resolve)方法实现了双亲委派法。具体步骤为:

  1. 通过findLoadedClass方法检查当前类加载器是否加载过该类,加载过就直接返回该Class对象。
  2. 如果没有加载过就将该加载请求委托给父加载器,如果没有父加载器则委托给启动类加载器。
  3. 如果父加载器无法完成请求则自己使用findClass方法加载该类

所以现在自定义类加载器想依照双亲委派模型去实现可以直接继承ClassLoader类并重写findClass方法。要是不想受照双亲委派模型约束可以直接重写loadClass方法。

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

推荐阅读更多精彩内容