jvm(二)类加载过程与类加载器

本篇内容:

  • 类加载过程
  • 类加载器分类
  • 双亲委托机制
  • 沙箱安全机制
类加载过程

类加载过程

类加载器子系统负责将从文件系统或者网络中加载Class文件到内存中。jvm通过按需加载的方式加载class文件,并且只加载一次。加载的类信息存放在方法区的内存空间,除了类信息外,方法区还会存放运行时常量池信息,可能还包括字符串字面值或数字常量(这部分常量信息是Class文件中常量池部分的内存映射)。
类的加载过程:加载->链接(验证、准备、解析)->初始化

  • 加载:通过类加载器将.class文件加载进内存,在内存中生成一个Class对象(java.lang.Class),作为方法区这个类的各种数据的访问入口。
    虚拟机在加载阶段做了三件事情:(1)通过一个类的全类名来获取此类的二进制字节流。(2)将这个字节流所代表的静态存储结构转化成方法区的运行时数据结构。(3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  • 验证:确保Class文件中的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机。
    主要完成4个检验动作:(1)文件格式验证:验证字节流是否符合Class文件格式的规范,能被当前版本虚拟机处理。主要目的是保证输入的字节流能正确的解析并存储在方法区上,通过验证后,字节流才能进入内存的方法区进行存储,后面的是那个阶段全部是基于方法区的存储结构进行的,不会再直接操作字节流了。(2)元数据验证:对类的元数据信息进行语义校验,保证描述的信息符合java语言规范。(3)字节码验证:第二阶段对元数据信息中的数据类型做完校验后,接着就是对类的方法体进行校验分析,保证被校验类的方法在运行时不会危害虚拟机。(4)符号引用校验:发生在将符号引用转化成直接引用的时候,也就是解析阶段。注意:验证阶段是非常重要的,但不是必要的,如果确保代码没问题,可以通过设置参数-Xverify:none参数来关闭大部分验证措施,来缩短虚拟机类加载时间。
  • 准备:为类变量分配内存并设置初始值,基本类型的初始值为0,也能用类型的初始值为null。这些变量所使用的内存都将在方法区进行分配,另外这里分配的类变量,而不是实例变量,实例变量是在对象实例化后随对象一起分配到java堆内存中。通常情况下初始值为0,还有特殊情况,在类字段的字段属性中存在ContantValue属性的话,在准备阶段变量的值就会被初始化成指定的值。static final修饰的字段就会在编译期生成ContantValue属性,在类加载的准备阶段直接把constantValue的值赋给该字段。
  • 解析:将常量池内的符号引用替换成直接引用的过程。符号引用:是任意形式的字面量,用一组符号来描述引用的目标,只要无歧义的定位到目标即可,与内存布局无关。直接引用:是直接指向模具表的指针,相对偏移量或是一个能间接定位到目标的句柄,与内存布局有关。
  • 初始化:执行类构造器<clinit>()方法的过程,对static修饰的变量进行初始化,没有类变量的情况下。<init>是对象构造器,对非静态变量解析初始化,而<clinit>是类构造器对静态变量,对静态代码块进行初始化。
    示例:
public class Test {

    private int num_1 = 11;

    private static int num_2 = 22;

    private static final int num_3 = 33;

    private int num_4;

    public Test(){
        this.num_4 = 44;
    }

}
反编译字节码.png

类加载器分类

类加载器
  • 引导类加载器(启动类加载器,Bootstrap ClassLoader)
    (1)使用c/c++实现,嵌套于jvm内部,
    (2)用来加载java核心类库,提供jvm自身需要的类(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容)
    (3)不继承java.lang.ClassLoader,没有父加载器
    (4)加载扩展类和应用程序加载器,并指定为他们的父类加载器
    (5)Bootstrap加载器只加载包名为java、javax、sun等开头的类
  • 扩展类加载器(Extension ClassLoader)
    (1)java语言编写,由sun.misc.Launcher$extClassLoader实现
    (2)派生于ClassLoader抽象类
    (3)父类加载器为引导类加载器
    (4)从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录,也会自动由扩展类加载器加载。
  • 应用程序类加载器(系统类加载器 AppClassLoader)
    (1)java语言编写,由sun.misc.Launcher$AppClassLoader实现
    (2)派生于ClassLoader抽象类
    (3)负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
    (4)是程序默认的类加载器,一般来说,java应用都是由应用程序类加载器来加载
//类加载器
public class ClassLoaderTest {
    public static void main(String[] args) {

        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //获取其上层:扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d

        //获取其上层:获取不到引导类加载器
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);//null

        //对于用户自定义类来说:默认使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);//null


    }
}
输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@61bbe9ba
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

Process finished with exit code 0


//类加载器的加载路径
public class ClassLoaderTestPath {
    public static void main(String[] args) {
        System.out.println("**********启动类加载器**************");
        //获取BootstrapClassLoader能够加载的api的路径
        URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urLs) {
            System.out.println(element.toExternalForm());
        }
        //从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);

        System.out.println("***********扩展类加载器*************");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")) {
            System.out.println(path);
        }

        System.out.println("***********应用程序类加载器*************");
        //从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d

    }
}
输出结果:
**********启动类加载器**************
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/classes
null
***********扩展类加载器*************
/Users/cuiqingdong/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
***********应用程序类加载器*************
sun.misc.Launcher$ExtClassLoader@355da254

双亲委派机制

双亲委派机制

工作原理:
(1)如果一个类加载器收到类加载的请求,它并不会自己先去加载,而是将这个请求委托给父类的加载器去执行
(2)如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的引导类加载器
(3)如果父类加载器可以完成类加载任务,就成功返回,如果父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委托机制。
优势:
(1)避免类的重复加载
(2)防止核心api被随意篡改

沙箱安全机制

自定义String类,但是在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java/lang/String.class),报错信息说没有main方法,就是因为加载的是rt.jar包中的String类,这样保证对java核心源代码的保护,就是沙箱安全机制。

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