JVM类加载机制

类加载机制

流程:

​ jvm通过类加载器将Class文件通过二进制数据读取到内存中,放到方法区中,并且在堆中创建类的对象,方法区中存的相当于这个类运行时的数据结构,这写数据结构是通过堆中类对象进行访问的。

类的生命周期

​ 加载-->验证-->准备-->解析-->初始化-->使用-->卸载

​ 其中 加载,验证,准备,初始化是按照顺序执行的,解析可能在某种情况下在初始化之后执行,这也是为了支持Java的动态绑定

动态绑定

/**
 *
 * @author QuinnNorris
 * 域不具有多态性
 */
public class polymorphics {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Super sup = new Sub();//这儿的类型的Super
        System.out.println("sup.field = " + sup.field + ", sup.getField() = "
                + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.field = " + sub.field + ", sub.getField() = "
                + sub.getField() + ", sub.getSuperField() = "
                + sub.getSuperField());
    }

}

class Super {
    public int field = 0;

    public int getField() {
        return field;
    }
}

class Sub extends Super {
    public int field = 1;

    public int getField() {
        return field;
    }

    public int getSuperField() {
        return super.field;
    }
}

结果:

sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0

也就是普通方法是根据实例来执行的,但是属性和静态方法是根据类型来执行的。

加载

  • 通过全限定类名将其转换为二进制流
  • 将二进制流转换为方法区中的运行时数据结构
  • 堆中生成一个类,用来访问方法区中的运行时数据结构验证

验证

确保加载的类的正确性

  • 文件格式验证 开头有个标识
  • 元数据验证
  • 字节码验证
  • 符号引用验证

准备

为类的静态变量分配内存,将其初始化为默认值

默认值是根据类型来确定的比如 int是0,String是null

解析

解析是将类中的符号引用转换为直接引用(符号引用和直接引用)

  • 符号引用:一组符号来描述目标,可以是任何字面量
  • 直接引用:指针,偏移量,或者指向目标的句柄

初始化

上面准备是将静态变量赋予了默认值,初始化就是赋予正确的值。

初始值设定了两种方式:

  • 声明类变量的时候指定初始值
  • 使用静态代码块为类变量设置初始值
jvm初始化步骤
  • 先加载类
  • 看这个类是否有父类,有的话先初始化其父类
  • 依次执行类中的初始化语句
类初始化
  • new一个实例的时候(包括clone,反序列化等)
  • 调用静态方法的时候
  • 调用静态属性的时候
  • 实例化子类的时候
  • 反射(Class.forName("全限定类名"))
  • 作为启动类的时候

补充一个初始化方法的执行顺序

/**
 * @program: threadpooltest
 * @Date: 2020/1/8 14:36
 * @Author: lvjiangpeng
 * @Description:
 */
public class Test {
    static {
        System.out.println("启动类 static method");
    }
    {
        System.out.println("启动类 method");
    }
    public Test(){
        System.out.println("启动类 construction");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        Baby baby = new Baby();//实例化子类,
//        Father.say();//调用静态方法
//        Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.jvmDay01.Father");
        System.out.println(Father.name);//调用静态属性
    }
}

class Father{
    public static String name = "猪妈妈";
    public static void say(){
        System.out.println("Father static say method");
    }
    static {
        System.out.println("Father static method");
    }
    public Father(){
        System.out.println("Father construction");
    }
    {
        System.out.println("Father method");
    }
}

class Mather{
    static {
        System.out.println("Mather static method");
    }
    public Mather(){
        System.out.println("Mather construction");
    }
    {
        System.out.println("Mather method");
    }
}

class Baby extends Mather{
    static {
        System.out.println("baby static method");
    }
    {
        System.out.println("baby method");
    }
    public Baby(){
        System.out.println("Baby construction");
    }
}

输出:

启动类 static method
Mather static method
baby static method
Mather method
Mather construction
baby method
Baby construction
Father static method
猪妈妈
  1. 启动类的静态代码块
  2. 父类的静态代码块
  3. 子类静态代码块
  4. 父类的代码块
  5. 父类的构造函数
  6. 子类的代码块
  7. 子类的构造函数

结束生命周期

  • 程序正常执行结束
  • 遇到异常导致程序终止
  • 程序出错导致虚拟机终止
  • 执行System.exit()方法

类加载器

  • 启动类加载器 Bootstrap ClassLoader(C++)
    • 加载JDK\jre\lib 和 -Xbootclasspath指定目录下的类
    • 无法直接被java程序引用
  • 扩展类加载器 ExtClassLoader(Java)
    • 加载JDK\jre\lib\ext 或者 java.ext.dirs系统变量指定路径的类(如javax开头的类)
  • 应用类加载器 AppClassLoader(Java)
    • 加载用户类路径指定的类(ClassPath),程序的默认类加载器
  • 自定义类加载器
JVM加载机制
  • 全盘负责,一个类加载器加载某个类的时候,这个类依赖和引用的其他类也由其负责,除非指定另一个加载器来载入。
  • 双亲委派,先由父类加载,父类加载不了自己再尝试加载
  • 缓存机制,每个加载的类都会被缓存,使用类的时候都是先去缓存查找,缓存没有再加载然后缓存,所以更改了Class之后需要重启jvm。

类的加载

  • 启动程序的时候由JVM初始化加载
  • Class.forName()反射加载
  • ClassLoader loader = 类.class.getClassLoader()
    • loader.loaderClass();
public class BaseTest {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * ClassLoader.loaderClass()  只是将类加载到jvm中,不会执行静态代码块,只有newInstance的时候才会执行
         * Class.forName()将类加载到jvm中,并且进行解释,执行静态代码块
         * Class.forName(name,initializa,loader)将类加载到jvm中,带的参数可以控制是否执行静态代码块
         */
        ClassLoader loader = HelloWorld.class.getClassLoader();
        /**
         *Process finished with exit code 0  不会执行初始化块
         */
        loader.loadClass("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2");
        /**
         * Test2 static code 会执行初始化块
         */
//        Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2");
        /**、
         *指定类加载器,初始化时不执行静态块
         */
//        Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2",false,loader);
        Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2",true,loader);
    }
}
class HelloWorld {
    static {
        System.out.println("helloworld static code");
    }
    public HelloWorld(){
        System.out.println("helloworld construction");
    }
}

class Test2 {
    static {
        System.out.println(" Test2 static code");

    }
    public Test2(){
        System.out.println("Test2 构造函数");
    }
}

注:这是学习笔记 通过学习 纯洁的微笑 JVM相关文章以及网上相关文章做的笔记,有不对的地方还望多多指教。

posted @ 2020-01-08 15:42  嘿,笑一个  阅读(181)  评论(0编辑  收藏  举报