栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

001—JVM类加载子系统讲解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

001—JVM类加载子系统讲解

JVM

 

类加载器子系统

类加载子系统的作用

类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识

ClassLoader只负责class文件的加载,至于它是否可以运行,则有Execution Engine决定

加载的类信息存放与一块称为方法区的内存空间,除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面类和数字常量(这部分常量信息是Class文件常量池部分的内存映射)

 类加载的过程

加载:

  1.         通过一个类的全限定名获取定义此类的二进制字节流(在磁盘上以流的方式)
  2.         将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3.         在内存生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
链接过程:         验证: (verifyerror)

                        1>目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载的正确性,不会危害虚拟机自身安全

                       2> 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证

         准备:     (此阶段没有默认值比如 a = 1,此阶段 a = 0)

                        1>为类变量分配内存并且设置该类变量的默认初始化值,即零值

                         2>这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化

                          3>这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到java堆中的

          解析:

                           1>将常量池内的符号引用转换为直接引用的过程

                            2>事实上,解析操作往往会伴随着JVM在执行完初始化之后执行

                            3>符号引用就是一组符号来描述所引用的目标。直接引用就是直接指向目标的指针,相对偏移量或一公分间接定位到目标的句柄

                             4>解析动作主要针对类或接口字段,类方法,接口方法,方法类型。

初始化

1>初始化阶段就是执行类构造器方法{}的过程  (不同与类的构造器)

此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来的(执行类构造器方法作用) (如果没有这样的操作则不会生成 clinit方法)

2>构造器方法中指令按语句在源文件中出现的顺序执行

不同于类的构造器  (构造器是虚拟机视角下的init)

3>若该类具有父类,JVM保证子类的执行前,父类的的已经执行完毕

4>虚拟机必须保证一个类的方法在多线程下被同步加锁 (clinit作用的是静态的东西  静态是共享的)

 字节码文件中的init是构造器方法
  private static int num = 1;
    static {
        num = 2;
        number = 20;
    }
    private static int number = 10; //linking 的prepare阶段已经将number = 0赋值了

    public static void main(String[] args) {
        System.out.println(ClassInitTest.num);
    }
private static int num = 1;
    static {
        num = 2;
        number = 20;
        System.out.println(num);
        //声明在后的变量可以赋值,但是不能调用
        //System.out.println(number);//Illegal forward reference 非法前向引用
    }
    private static int number = 10;

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

 

 

虚拟机自带的类加载器: 启动类加载器(Bootstrap)引导类加载器

这个类使用C/C++语言实现的,嵌套在JVM内部

它用来加载JAVA的核心库,用于提供JVM自身需要的类

并不继承自java.lang.ClassLoader,没有父加载器

加载扩展类和应用程序类加载器,并指定为他们的弗雷加载器

出于安全考虑,bootstrap启动类只加载报名为java,javax,sun等开头的类

虚拟机自带的加载器 扩展类加载器(Extension ClassLoader)加载核心包之外的jar包

Java语言编写,由sun.misc.Launcher$ExtClassLoader实现

派生于ClassLoader类

从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下在家类库,如果用户创建的JAR放在次目录下,也会自动由扩展类加载器加载;

应用程序类加载器(系统类加载器 AppClassLoader)

java语言编写,由sun.misc.Launcher$AppClassLoader实现

派生于ClassLoader类

弗雷加载器为扩展类加载器

它负责加载环境变量classpath或系统属性java.class.path指的是路径下的类库

该类加载是程序中默认的加载器,一般来说,java应用的类都是由它来完成加载

通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器

jar文件中存放的都是.class文件 系统类加载器
//获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("systemClassLoader = " + systemClassLoader);
        //获取其上层:扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println("extClassLoader = " + extClassLoader);

        //获取其上层
        ClassLoader bootstrpClassLoader = extClassLoader.getParent();
        System.out.println("bootstrpClassLoader = " + bootstrpClassLoader);


        //对于用户自定义类来说
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println("classLoader = " + classLoader);

        //String类使用引导类加载器进行加载的-->Java的核心类库都是使用引导类加载器进行的
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println("classLoader1 = " + classLoader1);
 类加载器能够加载的文件路径
System.out.println("--------启动类加载器------------");
        //获取BootStrapClassLoader能够加载的api的路径
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urls){
            System.out.println(element.toExternalForm());
        }
        //从上面的路径随意选择一个类,看看他的类加载器是什么?引导类加载器
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println("classLoader = " + classLoader);

        System.out.println("----------扩展类加载器------------");
        //扩展类加载其能加载的路径都有哪些?
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")){
            System.out.println(path);
        }
        //从上面的路径随意选择一个类,看看他的类加载器是什么?//扩展加载器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println("classLoader1 = " + classLoader1); //sun.misc.Launcher$ExtClassLoader@1453f44
用户自定义类加载器

作用:

        隔离加载类 (中间件 类冲突 类仲裁 )

        修改类加载的方式  

        扩展加载源

        防止源码泄漏 (防止代码被修改)

自定义类加载器的步骤
  • 继承抽象类java.lang.ClassLoader类的方式
  • 自定义的类加载逻辑写在findClass()方法中
  • 可以直接基层URLClassLoader类,这样就避免自己编写findClass()方法
jar文件中存放的都是.class文件
public class CustomClassLoader extends ClassLoader{

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        try {
            //读到内存中形成一个字节数组
            byte [] result = getClassCustomPath(name);
            if (result == null){
                throw new FileNotFoundException();
            }else {
                return defineClass(name, result, 0, result.length);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    private byte[] getClassCustomPath(String name) {
        //从自定义路径中加载指定类:
        //如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作
        return null;
    }
}
findClass与defineClass要配合使用

ClassLoader类是一个抽象类,其后所有的类加载器都继承于ClassLoader

(但是不包括启动类加载器)

 ClassLoader的获取
public static void main(String[] args) {
        try {
            //1:
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println("classLoader = " + classLoader);
            //2:
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            System.out.println("contextClassLoader = " + contextClassLoader);
            ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();
            System.out.println("classLoader2 = " + classLoader2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
双亲委派机制

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时,才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,java虚拟机采用的时双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。

双亲委派机制工作原理

               1>如果一个类加载器收到了类加载器请求,它并不会自己先去加载,而是

把这个请求委托给父类的加载器去执行;

                2>如果父类加载器还存在其夫雷加载器,则进一步向上委派,依次递归

最终达到顶层的启动类加载器

                3>如果父类加载器可以完成类加载雷武,就成功返回,倘若父类加载器无法完成此加载任务,自家在其才会尝试自己去加载,这就是双亲委派模式

 如下代码找不到main方法:如果自定义下的java.lang包中创建String类,根据双亲委派机制,会向上找到类加载器,而在java核心包的String中没有main方法,因此找不到程序的入口,就没有run方法,无法运行

 双亲委派机制 示例二

 如果程序要加载jdbc.jar 根据双亲委派机制,会找到引导类加载器,引导类加载器会加载核心的rt.jar包,但同时需要加载第三方库,此时就会反向委托类加载器找到系统类加载器,然后再根据线程上下文类加载器ContextClassLoader加载jdbc.jar

双亲委派机制优势

 根据双亲委派机制,如果在自定义的java.lang的包下创建一个自定义类如Test,根据双亲委派机制,就会想上找找到引导类而引导类一看当前的包是java.lang属于管辖范围,此时就会直接给出异常,由此说明,引导类加载器是由使用的权限的。一般是加载rt.jar核心包。

优势:

  •         避免类的重复加载
  •         保护程序安全,防止核心API被随意篡改

                自定义类:java.lang.String

沙箱安全机制

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

在JVM中表示两个class对象是否是同一个类存在的两个必要条件:

        类的完整类名必须一致,包括包名

        加载这个类的ClassLoader(值ClassLoader实例对象)必须相同

换言之,JVM中,即使这两个类对象来源于同一个Class文件,被同一个虚拟机所加载,但只要加载他们的ClassLoader实例对象不同,那么这两个类的对象也是不相等的。

双类加载器的引用

        JVM必须知道一个类型是由启动加载器加载的还是用户加载器加载的,如果一个类型是由用户类加载器加载 ,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中,当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类型的加载器是相同的。

类的主动使用和被动使用

Java程序对类的使用方式分为:主动使用和被动使用

        主动使用:

  1.                 创建类的实例
  2.                 访问某个类或接口的静态变量,或者对该静态变量赋值
  3.                 调用类的静态方法
  4.                 反射 比如(Class.forName())
  5.                 初始化一个子类
  6.                 Java虚拟机启动时被表明为启动类的
  7.                 JDK7开始提供的动态语言支持
除了以上其中,其他使用java类的方式被看作是对类的被动使用

区别在于会不会导致类的初始化

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/325390.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号