• JVM类加载机制


    类的加载过程

    1、类的加载

    • 将 字节码文件( .class ) 读入到 JVM 所管理的内存中
    • 将 字节码文件对应的类的数据结构 保存在方法区
    • 最后生成一个与该类对应的 java.lang.Class 类型的对象 ( 在堆区 )

    2、类的链接

    • 连接是把已读入到内存的类的二进制数据合并到Java运行时环境(JRE)中去。
    • 连接又分为三个阶段:验证、准备、解析。
    • 验证:验保证类有正确的内部结构,并且与其它类协调一致如果JVM 检查到错误,就会抛出Error 对象。

        类文件的结构检查: 确保文件遵循Java 文件的固定格式
        语义检查: 确保类本身符合Java 语言的语法规定
        字节码验证: 确保字节码流可以被JVM 安全地执行

    • 准备:  在准备阶段,JVM 为类的静态变量分配内存,并设置默认值(byte 、short 、int 默认值都是 0,long 默认值是 0L,float 默认值是 0.0F,double 默认值是 0.0,boolean 默认值是 false,char 默认值是 u0000,  引用类型的默认值是 null)。
    • 解析: 将符号引用解析为直接引用

    3、类的初始化

    • 初始化阶段,JVM执行类的初始化语句,为静态变量赋予初始值
    • 静态变量的初始化途径:在静态变量的声明处进行初始化,在静态代码块中进行初始化
    • 初始化代码可能是(声明变量时的赋值语句): protected static int a = 100 ;也可以是(静态代码块):

         static {

        a = 10000 ;
               }

    • 类初始化的一般步骤

        如果该类还没有被加载和连接,那么先加载和连接该类
        如果该类存在直接父类,但该父类还未初始化,则先初始化其直接父类
        如果该类中存在初始化语句,则依次执行这些初始化语句

    • 类的初始化时机

        JVM 只有在首次主动使用某个类或接口时才会初始化它
        被动使用不会导致本类的初始化

    诡异的问题 解析测试案例:

    复制代码
    package ecut.classloader;
    
    public class Sun {
        
        protected static int a = 100 ;//链接(准备):0//初始化: a:100
        protected static int b  ;//链接(准备):0//初始化: b:0
        
        protected static Sun instance = new Sun() ;//链接(准备):null//初始化: a:101  b:1    
        
        public Sun() {
            a++ ;
            b++ ;
        }
        
    }
    复制代码
    复制代码
    package ecut.classloader;
    
    public class Moon {
        
        protected static Moon instance = new Moon() ;//链接(准备):null//初始化: a:1  b:1
        
        protected static int a = 100 ;//链接(准备):0//初始化: a:100
        protected static int b  ;//链接(准备):0//初始化: b:1
        
        public Moon() {
            a++ ;
            b++ ;
        }
        
    }
    复制代码

    4、类的使用

    主动使用会导致类被初始化

    • 创建类的实例 ( new 、反射、反序列化 、克隆 )
    • 调用类的静态方法
    • 访问类 或 接口的 静态属性 ( 非常量属性 )  ( 取值 或 赋值 都算 )
    • 访问类 或 接口 的 非编译时常量,也将导致类被初始化:public static final long time = System.currentTimeMillis();
    • 调用反射中的某个些方法,比如 Class.forName( "edu.ecut.Student" );
    • 初始化某个类时,如果该类有父类,那么父类将也被初始化
    • 被标记为启动类的那些类(main)

    被动使用不会导致类被初始化

    • 程序中对编译时常量的使用视作对类的被动使用
    • JVM初始化某个类时,要求其所有父类都已经被初始化,但是 该规则不适用 于 接口 类型
    • 只有当程序访问的静态变量或静态方法的确在当前类或接口定义时,才能看作是对类或接口的主动使用,比如使用了 Sub.method() ,而 method() 是继承自 Base ,则只初始化 Base 类
    • 调用 ClassLoader 的 loadClass( ) 加载一个类,不属于对类的主动使用

    5、类的卸载:当一个类不再被任何对象所使用时,JVM会卸载该类。

    类加载器的双亲委派模型
    1、父亲委托机制

    • 设loader 要加载A 类,则loader 首先委托自己的父加载器去加载A 类,如果父加载器能加载A 类则由父加载器加载,否则才由loader 本身来加载A类。
    • 这种机制能更好地保证Java 平台的安全性
    • 父亲委托机制中,每个类加载器都有且只有一个父加载器,除了JVM 自带的根类加载器( Bootstrap Loader )

    2、模型示意图

    根类加载器(BootstrapLoader)

    • 负责加载虚拟机的核心类库,比如java.lang.* 等
    • 从系统属性sun.boot.class.path 所指定的目录中加载类库
    • 该加载器没有父加载器,它属于JVM 的实现的一部分(用C++实现)

    扩展类加载器(ExtClassLoader)

    • 其父加载器为BootstrapLoader 类的一个实例
    • 该加载器负责从java.ext.dirs 系统属性所指定的目录中加载类库或者从JDK_HOME/jre/lib/ext 目录中加载类库
    • 该加载器对应的类是纯Java 类,其父类是java.lang.ClassLoader

    系统类加载器(AppClassLoader)

    • 也称作应用类加载器,其父加载器默认为ExtClassLoader 类的一个实例
    • 负责从CLASSPATH 或系统属性java.class.path 所指定的目录中加载类库
    • 它是用户自定义类加载器的默认父加载器
    • 其父类也是java.lang.ClassLoader

    过程

    • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的来加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的启动类加载器
    • 只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)的时候,子加载器才会尝试自己去加载

    使用这种模型的好处

    • java类随着它的来加载器一起具备了一种带有优先级的层次关系,对于保证java程序的稳定运作很重要
    • 代码集中在java.lang.ClassLoader的loadClass()方法中。

    牛客网笔试题

    1、What will happen when you attempt to compile and run the following code?

    public class Test{
    
    static{
    
       int x=5;
    
    }
    
    static int x,y;
    
    public static void main(String args[]){
    
       x--;
       myMethod( );
       System.out.println(x+y+ ++x);
    
    }
    
    public static void myMethod( ){
      y=x++ + ++x;
    
     }
    
    }

    正确答案: D  

    A、compiletime error

    B、prints:1

    C、prints:2

    D、prints:3

    E、prints:7

    F、prints:8

    2、有关静态初始化块说法正确的是?

    正确答案: A B C  

    A、无法直接调用静态初始化块

    B、在创建第一个实例前或引用任何静态成员之前,将自动调用静态初始化块来初始化

    C、静态初始化块既没有访问修饰符,也没有参数

    D、在程序中,用户可以控制合适执行静态初始化块

    3、子类A继承父类B, A a = new A(); 则父类B构造函数、父类B静态代码块、父类B非静态代码块、子类A构造函数、子类A静态代码块、子类A非静态代码块 执行的先后顺序是?

    正确答案: C   你的答案: C (正确)

    A、父类B静态代码块->父类B构造函数->子类A静态代码块->父类B非静态代码块->子类A构造函数->子类A非静态代码块

    B、父类B静态代码块->父类B构造函数->父类B非静态代码块->子类A静态代码块->子类A构造函数->子类A非静态代码块

    C、父类B静态代码块->子类A静态代码块->父类B非静态代码块->父类B构造函数->子类A非静态代码块->子类A构造函数

    D、父类B构造函数->父类B静态代码块->父类B非静态代码块->子类A静态代码块->子类A构造函数->子类A非静态代码块

    4、下面代码的输出是什么?

    public class Base
    {
        private String baseName = "base";
        public Base()
        {
            callName();
        }
     
        public void callName()
        {
            System. out. println(baseName);
        }
     
        static class Sub extends Base
        {
            private String baseName = "sub";
            public void callName()
            {
                System. out. println (baseName) ;
            }
        }
        public static void main(String[] args)
        {
            Base b = new Sub();
        }    
    }

    正确答案: A 

    A、null

    B、sub

    C、base

    解析:

    • new Sub();在创造派生类的过程中首先创建基类对象,然后才能创建派生类。
    • 创建基类即默认调用Base()方法,在方法中调用callName()方法,由于派生类中存在此方法,则被调用的callName()方法是派生类中的方法,此时派生类还未构造,所以变量baseName的值为null

    5、What will be printed when you execute the following code?

    class C {
        C() {
            System.out.print("C");
        }
    }
     
    class A {
        C c = new C();
     
        A() {
            this("A");
            System.out.print("A");
        }
     
        A(String s) {
            System.out.print(s);
        }
    }
     
    class Test extends A {
        Test() {
            super("B");
            System.out.print("B");
        }
     
        public static void main(String[] args) {
            new Test();
        }
    }

    正确答案: B  

    A、BB

    B、CBB

    C、BAB

    D、None of the above

    转载请于明显处标明出处

    http://www.cnblogs.com/AmyZheng/p/8647217.html

  • 相关阅读:
    NYOJ 158 省赛来了(变相组合数)
    NYOJ 111 分数加减法
    NYOJ 14 会场安排问题 (贪心)
    POJ 3903 Stock Exchange(LIS)
    NYOJ 456 邮票分你一半(01背包)
    HDU 4521 小明系列问题——小明序列 (LIS加强版)
    CSU 1120 病毒(经典模板例题:最长公共递增子序列)
    挑战程序设计竞赛里面的几道深度优先搜索
    2009 Multi-University Training Contest 4
    USACO sec1.1
  • 原文地址:https://www.cnblogs.com/AmyZheng/p/9412114.html
Copyright © 2020-2023  润新知