2021.03.26JVM-类加载

当我们运行程序的时候,首先是.java文件,我们通过编译成.class文件后进行类加载。

一:首先我们jvm虚拟机是一开始将所有的类都加载进来吗?

      一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载。

     我们知道java程序运行的时候,所有类必须被加载到jvm虚拟机中才可以正常使用

二:什么是类加载

      类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。

     类加载就是我们将.class文件加入到内存中。

三:类加载器的作用

      类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。

     类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。

     类加载的作用就是加载class文件---

四:怎么获取类加载器呢?

     启动类加载器:负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java   程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可。
    扩展类加载器:这个加载器由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
    应用程序类加载器:这个类加载器是由sun.misc.Launcher$AppClassLoader实现的。由于这个类加载器是ClassLoader中的getSystemClassLoader方法的返回值,所以也叫系统类加载器。它负责加载用户类路径上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器

首先通过反射进行获取类加载器

public class Sppt {
    public int  a;
    public static void main(String[] args) {
        //类是模板,对象是具体的
        Sppt sppt=new Sppt();
        Sppt sppt1=new Sppt();
        Sppt sppt2=new Sppt();
        System.out.println(sppt.hashCode());
        System.out.println(sppt1.hashCode());
        System.out.println(sppt2.hashCode());
        Class spptClass=sppt.getClass();//通过反射获取类对象
        ClassLoader  classLoader=spptClass.getClassLoader();//AppClasLoader 用户程序的类加载器
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());//ExtClassloader(扩展类加载器)\jre\lib\ext我们获取用户程序的类加载器的父类
        System.out.println(classLoader.getParent().getParent());//null(我们java获取不到)获取用户程序类加载器父类的父类

    }
}
168423058
821270929
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@452b3a41
null

五:当我们进行类加载

     new一个对象,这时候对象引用在栈里面,正真的实例在堆中,我们栈中只是保留了在堆中的地址(堆中存放对象的属性等等)。

     总结一句话:栈是目录,堆是内容。

     这里插一句反射:我们通过  对象.get class();   得到的反射,不论对象是类的哪个对象,我们得到的就是相同的模板类,他们的hashcode值是相同的。
类的上面我们可以得到类加载器 通过反射得到的对象  对象.getClassloader()。得到了类加载器。
当我们new对象的时候,首先我们找的是应用类,然后向上找扩展类加载器再往上找去根类加载器。如果根加载器中没有就用扩展类加载器,扩展类加载器没有就用当前应用类中的。(扩展类加载器就是lib下的ext里的jar包)
(根加载器在rt.jar包里面)

 

 六:双亲委派机制

保证安全:顺序先从当前类加载器---->扩展类加载器---->根加载器

找的顺序是先从当前类加载器中找不管有没有继续从扩展类加载器中找,不管有没有继续从根加载器中找,如果根加载器中有就执行根加载的,如果没有就看看扩展类加载 器中有没有,有的话执行,没有的话再看看当前类中有没有。

public class String {
//双亲委派机制
//我们首先看1app--》Exc---》BOOT(最终执行)
    public String toString(){
        return "hello";
    }
    public static void main(String[] args) {
    String s =new String();
    s.toString();
    }
}
//就像这种就会出错,因为String在在Boot中有

像这种就可以:

public class String1{
    public String toString(){
        return "hello";
    }
    public static void main(String[] args) {
    String1 s =new String1();
        System.out.println(s.toString());
    }
}//输出hello

1.类加载器收到类加载的请求  Application

2.将这个请求向上委托给父类加载器去完成,一直向上委托,知道启动类加载器。BOOT

3.启动加载器检查是否能够加载当前这个类BOOT,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载(如果BOOT有的话,就Boot,EXc有的就用EXC进行)

4.重复步骤3

类加载器的双亲委派加载机制(重点):

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

posted @ 2021-03-28 21:17  iLisa  阅读(55)  评论(0编辑  收藏  举报