zoukankan      html  css  js  c++  java
  • JVM类加载器及Java类的生命周期

    预定义类加载器(三种)

    启动(Bootstrap)类加载器:
      是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。
    由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
    扩展扩展(Extension)类加载器:
      是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
    系统(System)类加载器:

      是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

    双亲委派机制:
      某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。这是一种代理方式。

    双亲委派意义:
      系统类防止内存中出现多份同样的字节码
      保证Java程序安全稳定运行

    线程上下文类加载器(特殊):
      破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。
    类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。
    如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

    1.当高层提供了统一接口让低层去实现,同时又要是在高层加载(或实例化)低层的类时,必须通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。
    2.当使用本类托管类加载,然而加载本类的ClassLoader未知时,为了隔离不同的调用者,可以取调用者各自的线程上下文类加载器代为托管。

    几点问题:

      启动(Bootstrap)类加载器它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。

      Java 虚拟机是如何判定两个 Java 类是相同的?

      Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。

    eg:

    public void testClassIdentity() { 
       String classDataRootPath = "C:\workspace\Classloader\classData"; 
       FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); 
       FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); 
       String className = "com.example.Sample";    
       try { 
           Class<?> class1 = fscl1.loadClass(className); 
           Object obj1 = class1.newInstance(); 
           Class<?> class2 = fscl2.loadClass(className); 
           Object obj2 = class2.newInstance(); 
           Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); 
           setSampleMethod.invoke(obj1, obj2); 
       } catch (Exception e) { 
           e.printStackTrace(); 
       } 
    }

      代码中使用了类 FileSystemClassLoader的两个不同实例来分别加载类 com.example.Sample,得到了两个不同的 java.lang.Class的实例,接着通过 newInstance()方法分别生成了两个类的对象 obj1和 obj2,最后通过 Java 的反射 API 在对象 obj1上调用方法 setSample,试图把对象 obj2赋值给 obj1内部的 instance对象

    运行结果
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)
    at classloader.ClassIdentity.main(ClassIdentity.java:9)
    Caused by: java.lang.ClassCastException: com.example.Sample
    cannot be cast to com.example.Sample
    at com.example.Sample.setSample(Sample.java:7)
    ... 6 more

      给出的运行结果可以看到,运行时抛出了 java.lang.ClassCastException异常。虽然两个对象 obj1和 obj2的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被 Java 虚拟机认为是相同的。

    Java类的生命周期:

      类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。

    加载(Loading):

      就是将源文件的class文件找到类的信息将其加载到方法区中,
    然后在堆区中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。

    连接(Linking):
      验证:确定该类是否符合java语言的规范,有没有属性和行为的重复,继承是否合理,总之,就是保证jvm能够执行
      准备:主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值
        (1.八种基本数据类型默认的初始值是0
           2.引用类型默认的初始值是null
           3.有static final修饰的会直接赋值,例如:static final int x=10;则默认就是10.)
      解析:这一阶段的任务就是把常量池中的符号引用转换为直接引用,说白了就是jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。

    初始化(Initialization)
      这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化,执行的顺序就是:
    父类静态域或着静态代码块,然后是子类静态域或者子类静态代码块

    使用(Using)
      在类的使用过程中依然存在三步:对象实例化、垃圾收集、对象终结

    卸载(Unloading)
      类的生命周期走到了最后一步,程序中不再有该类的引用,该类也就会被JVM执行垃圾回收,从此生命结束。

    参考文章:

    https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

    http://blog.csdn.net/yangcheng33/article/details/52631940

  • 相关阅读:
    R 语言中的数据结构
    minimap2 长reads比对工具
    seqtk 一款快速处理fasta/fastq 文件的小程序
    Eclipse R语言开发环境搭建 StatET插件
    Windows 安装R
    HttpClient 发送请求和参数
    Java Base64编码和解码
    docker 安装
    docker
    inotify 监控文件系统操作
  • 原文地址:https://www.cnblogs.com/daily-note/p/8548904.html
Copyright © 2011-2022 走看看