廊坊万达:JVM入门--类加载器

admin 5个月前 (12-10) 科技 118 1

一、基础架构

概览

我们平时说的栈是指的Java栈,native method stack 内里装的都是native方式

细节架构图

二、类加载器

1、类的加载

廊坊万达:JVM入门--类加载器 第1张

廊坊万达:JVM入门--类加载器 第2张

  • 方式区并不是存放方式的区域,其是存放类的形貌信息(模板)的地方
  • Class loader只是卖力class文件的加载,相当于快递员,这个“快递员”并不是只有一家,Class loader有多种
  • 加载之前是“class”,加载之后就变成了“Class”,这是安装java.lang.Class模板天生了一个实例。“Class”就装载在方式区,模板实例化之后就获得n个相同的工具
  • JVM并不是通过检查文件后缀是不是.class来判断是否需要加载的,而是通过文件开头的特定文件标志廊坊万达:JVM入门--类加载器 第3张

2、类的加载历程

廊坊万达:JVM入门--类加载器 第4张

注重:加载阶段失败会直接抛出异常

2.1、加载

​ 把.class文件读入到java虚拟机中

  • 通过“类全名”来获取界说此类的二进制字节省

廊坊万达:JVM入门--类加载器 第5张

​ 动态编译:jsp-->java-->class

  • 将字节省所代表的静态存储结构转换为方式区的运行时数据结构

  • 在java堆中天生一个代表这个类的java.lang.Class工具,作为方式区这些数据的接见入口

2.2、链接

1. 验证
  • 确保class文件的字节省中包罗的信息相符当前虚拟机的要求,而且不会危害虚拟机自身平安。

  • 验证阶段主要包罗四个磨练历程:文件花样验证、元数据验证、字节码验证和符号引用验证。

2. 准备
  • 类变量(静态变量)分配内存并设置类变量默认值-->0/false/null(不包罗final修饰的static,final修饰的变量会显示初始化
  • 在初始化之前,若使用了类变量,用到的是默认值,并非代码中赋值的值
  • 不会实例变量分配初始化、类变量分配在方式区中,实例变量会随工具分配到java堆中
3. 剖析

​ 虚拟机常量池内的符号引用替换为直接引用 ,类名、字段名、方式名--->详细内存地址或偏移量

2.3、初始化

1. 自动/被动使用

廊坊万达:JVM入门--类加载器 第6张

2. 初始化注重点
  • 类变量被赋值、实例变量被初始化

  • 每个类/接口被Java程序首次自动使用的时刻才会被java虚拟机初始化

  • 从上到下初始化

  • 初始化一个类时,要求它的父类都已经被初始化了(除接口)

    • 当初始化一个类的时刻并不会先初始化它实现的接口

    • 当初始化一个接口的时刻,并不会初始化它的父接口

      一个父接口并不会由于它的子接口或实现类的初始化而初始化,只有当首次使用其特定的静态变量时(即运行时常量,如接口中引用类型的变量)时才会初始化

3. 深入明白举例1
  • 对于静态字段来说,只有直接界说了该字段的类才会被初始化
  • 每个类在初始化前,必须先初始化其父类(除接口)
  • 追踪类的加载情形:-XX:+TraceClassLoading(+示意开启,-示意关闭)
  • 对于常量(这里指编译器确定的常量)来说,常量值在编译阶段会存入到挪用它的方式所在的类的常量池中,本质上挪用类没有直接引用到界说常量的类
  • 对于引用类型数组来说,其类型是由JVM在运行时代动态天生的,示意为[L+自界说类全类名(一维)这种形式
  • 准备阶段只是分配内存、赋默认值,初始化阶段才是真正的赋值(自己设定的值)
    • 初始化阶段是从上到小初始化赋值
public class ClassLoaderTest {
    public static void main(String[] args) {
        //单独测试下列语句
        //1.
        System.out.println(Child.str1);
        /*输出
        * Parent static block
        * hello I'm Parent
        */
        //2.
        System.out.println(Child.str2);
        /*输出
        * Parent static block
        * Child static block
        * hello I'm Child
        */
        //3.
        System.out.println(Parent.str3);
        /*输出
        * hello I'm Parent2
        * */
        //4.
        System.out.println(Parent.str4);
        /*输出
        * Parent static block
        * 78f59c0d-b91c-4e32-8109-dec5cb23aa13
        * */
        //5.
        Parent[] parents1=new Parent[1];
        System.out.println(parents1.getClass());
        Parent[][] parents2=new Parent[2][2];
        System.out.println(parents2.getClass());
        /*输出
        * class [Lcom.lx.Parent;
        * class [[Lcom.lx.Parent;
        * */
        //6.
        System.out.println(Singleton1.count1);
        System.out.println(Singleton1.count2);
        System.out.println(Singleton2.count1);
        System.out.println(Singleton2.count2);
        /*输出
        * 1,1,1,0
        * */
        
    }
}
class Parent{
    public static  String str1 = "hello I'm Parent";
	public static final String str3 = "hello I'm Parent2";
    public static final String str4 = UUID.randomUUID().toString();

    
    static {
        System.out.println("Parent static block");
    }
}
class Child extends Parent{
    public static  String str2 = "hello I'm Child";

    static {
        System.out.println("Child static block");
    }
}

class Singleton1 {
    public static int count1;
    public static int count2=0;
    public static Singleton1 singleton1=new Singleton1();

    public Singleton1() {
        count1++;
        count2++;
    }

    public Singleton1 getInstance(){
        return singleton1 ;
    }
}
class Singleton2 {
    public static int count1;
    public static Singleton2 singleton2=new Singleton2();

    public Singleton2() {
        count1++;
        count2++;
    }

    public static int count2=0;

    public Singleton2 getInstance(){
        return singleton2 ;
    }
}
4. 效果剖析
  1. Child属于被动使用,Parent是自动使用,以是只会初始化Parent
  2. Child属于自动使用,以是会初始化Child,由于初始化的类具有父类以是先初始化父类
  3. Parent并没有被使用到,str3的值在编译时代就被存入CLassLoaderTest这个挪用它的方式所在的类的常量池中,与Parent无关
  4. str4不是编译时代就能确定的常量,就不会放到挪用方式类的常量池中,在运行时自动使用Parent类进而需要初始化该类
  5. 没有对Parent类初始化,引用数组类型并非Parent类,而是jvm动态天生的class [Lcom.lx.Parent
  6. 首先接见Singleton的静态方式--》Singleton是自动使用--》先初始化
    1. 第一种:准备阶段给count1,2分配空间默认值已经为0了,此时给类变量singleton初始化,挪用组织方式,分别加一
    2. 第二种:同上,然则在给singleton初始化时,count2并未初始化,自增只是暂时的,随后就要对它初始化,以是在count2初始化前对他举行的操作时无效的。

类加载情形

情形1:

  • 加载object....类
  • 加载启动类
  • 加载父类
  • 加载子类

类的加载并非一定要该类被自动使用化

廊坊万达:JVM入门--类加载器 第7张

情形2:同上

情形3:

​ 自界说的类只加载了启动类(挪用常量的方式所在的类)

廊坊万达:JVM入门--类加载器 第8张

情形4:加载启动类以及Parent类

反编译效果

情形1:

廊坊万达:JVM入门--类加载器 第9张

情形2:类似1

情形3:没有引用到Parent类(界说常量的类)

廊坊万达:JVM入门--类加载器 第10张

廊坊万达:JVM入门--类加载器 第11张

情形4:类似1

5. 深入明白举例2

接口中界说的变量都是常量

常量又分为编译期常量和运行期常量,编译期常量的值在编译时代就可以确定,直接存储在了挪用类的常量池中,以是接见接口中的编译期常量并不会导致接口的初始化,只有接见接口中的运行期常量才会引起接口的初始化。

父接口并不会由于子接口或是实现类的初始化而初始化,当接见到了其特定的静态变量时(即运行时常量,如接口中引用类型的变量)才会初始化

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        System.out.println(new demo2().a);
        System.out.println("=====");
        System.out.println(son1.a);
        new demo1().show();
        System.out.println(demo1.str);
        System.out.println(son1.b);
        System.out.println(demo1.s);//System.out.println(son1.s);
        /*输出
        * father2 singleton
        * 1
        * =====
        * 1
        * show method
        * string
        * father1 singleton
        * com.lx.father1$1@1b6d3586
        * */
    }
}
interface father1{
    int a=1;
    void show();
    String str="string";
    Singleton1 s=new Singleton1(){
        {
            System.out.println("father1 singleton");
        }
    };
}
interface son1 extends father1 {
    int b=0;
    Singleton1 s1=new Singleton1(){
        {
            System.out.println("son1 singleton");
        }
    };
}
class demo1 implements father1{
    @Override
    public void show() {
            System.out.println("show method");
    }
}
class father2{
    int a=1;
    void show(){}
    String str="string";
    Singleton1 s=new Singleton1(){
        {
            System.out.println("father2 singleton");
        }
    };
}
class demo2 extends father2{

}
6. 效果剖析

​ 第3行:子类初始化前必须初始化父类

​ 第5-8行:接见到编译时常量(已经存入了挪用方式类的常量池中),不会导致初始化

​ 第9行: 接见了运行时常量,需要初始化界说该运行时常量的类

3、类加载器分类

廊坊万达:JVM入门--类加载器 第12张

一、java虚拟机自带的类加载器

  1. 启动类加载器(Bootstrap) ,C++所写,不是ClassLoader子类

    廊坊万达:JVM入门--类加载器 第13张

  2. 扩展类加载器(Extension) ,Java所写

    廊坊万达:JVM入门--类加载器 第14张

  3. 应用程序类加载器(AppClassLoader)。

    • 自界说类一样平常为系统(应用)类加载器加载

二、用户自界说的类加载器

import com.gmail.fxding2019.T;

public class  Test{
    //Test:查看类加载器
    public static void main(String[] args) {

        Object object = new Object();
        //查看是谁人“ClassLoader”(快递员把Object加载进来的)
        System.out.println(object.getClass().getClassLoader());
        //查看Object的加载器的上一层
        // error Exception in thread "main" java.lang.NullPointerException(已经是祖先了)
        //System.out.println(object.getClass().getClassLoader().getParent());

        System.out.println();

        Test t = new Test();
        System.out.println(t.getClass().getClassLoader().getParent().getParent());
        System.out.println(t.getClass().getClassLoader().getParent());
        System.out.println(t.getClass().getClassLoader());
    }
}

/*
*output:
* null
* 
* null
* sun.misc.Launcher$ExtClassLoader@4554617c
* sun.misc.Launcher$AppClassLoader@18b4aac2
* */
  • 若是是JDK自带的类(Object、String、ArrayList等),其使用的加载器是Bootstrap加载器;若是自己写的类,使用的是AppClassLoader加载器;Extension加载器是卖力将把java更新的程序包的类加载举行
  • 输出中,sun.misc.Launcher是JVM相关挪用的入口程序
  • Java加载器个数为3+1。前三个是系统自带的,用户可以定制类的加载方式,通过继续Java. lang. ClassLoader

4、双亲委派机制

Java虚拟机接纳按需加载的方式,当需要使用该类是才会去讲class文件加载到内存天生class工具,加载类是接纳的是双亲委派机制

自底向上检查类是否已经被加载

自顶向下实验加载类

廊坊万达:JVM入门--类加载器 第15张

原理图:

廊坊万达:JVM入门--类加载器 第14张

另外一种机制:

廊坊万达:JVM入门--类加载器 第17张

双亲委派优势:

  • 制止类的重复加载。
  • 珍爱程序平安、防止焦点api被恶意窜改(如下例子)

用户自界说的类加载器不可能加载到一个有父加载器加载的可靠类,从而防止不可靠恶意代码代替父加载器加载的可靠的代码。例如:Object类总是有跟类加载器加载,其他用户自界说的类加载器都不可能加载含有恶意代码的Object类

//测试加载器的加载顺序
package java.lang;

public class String {

    public static void main(String[] args) {

        System.out.println("hello world!");

    }
}

/*
* output:
* 错误: 在类 java.lang.String 中找不到 main 方式
* */

注释:

​ 交给启动类加载器之后(java.lang.String/由java开头的包名)归它管,以是它首先加载这个类(若是焦点api内没有改类也会报错),轮不到让系统类加载器去加载该类,即无法加载到自己所写的String类,焦点api中的String类没有main方式,以是会报错说找不到main方式

5、弥补:

类的实例化

  • 为新的工具分配内存
  • 为实例变量赋默认值
  • 为实例变量赋值(自己界说的)
  • 为其天生 / 方式或者说组织方式

判断为同一个类的必要条件

廊坊万达:JVM入门--类加载器 第18张

廊坊万达:JVM入门--类加载器 第18张

使用类加载器的缘故原由

廊坊万达:JVM入门--类加载器 第20张

自界说类加载器

廊坊万达:JVM入门--类加载器 第21张

获取类加载器方式:

廊坊万达:JVM入门--类加载器 第22张

沙箱平安机制

廊坊万达:JVM入门--类加载器 第23张

命名空间

廊坊万达:JVM入门--类加载器 第24张

廊坊万达:JVM入门--类加载器 第25张

loadClass方式

​ 通过挪用ClassLoader类的loadClass方式加载一个类,并不是对一个类的自动使用,不会导致初始化。

类的卸载

廊坊万达:JVM入门--类加载器 第26张

廊坊万达:JVM入门--类加载器 第27张

,

阳光在线

阳光在线www.slwgd.com(原诚信在线)现已开放阳光在线手机版下载。阳光在线游戏公平、公开、公正,用实力赢取信誉。

Allbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:廊坊万达:JVM入门--类加载器

网友评论

  • (*)

最新评论

  • 卡利充值 2020-12-10 00:03:40 回复

    皇冠即时比分www.huangguan.us是一个提供皇冠代理APP下载、皇冠会员APP下载、皇冠体育最新登录线路、新2皇冠网址的的体育平台。新皇冠体育官网是多年来值得广大客户信赖的平台,我们期待您的到来!看来挺好的

    1

站点信息

  • 文章总数:1252
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1645
  • 评论总数:1083
  • 浏览总数:113010