JVM(二)

什么是类加载

把类的.class文件中的二进制读入(加载)到内存中

类加载目的是什么

在堆中的方法区里面存储.class对象,把类的信息结构存到堆里面 , 首先说明方法区是在堆里面

类加载的信息存储到了那?

在这里插入图片描述

类加载是如何加载的?

类加载有两种形式,一种是jvm的,另一种是用户自定义的, jvm有3中, 用户自定义的只需要继承了classLoader就可以.

根加载器Bootstrap

根加载是jvm核心加载器,他是由C++来写的, 对于java.lang里的所有类都是属于跟加载器
他没有继承ClassLoader

扩展加载器Extension

他的父加载器是Bootstrap
继承了ClassLoader
一般加载的是系统属性或者jdk加载

系统类加载器 System

父加载器是扩展加载器Extension
继承了ClassLoader
根据classpath进行加载
自定义加载器属于系统类加载器

用户自定义加载器

如果用户想自定义加载器那么首先是继承ClassLoader,他的默认加载器属于是系统类加载器

加载的流程

加载流程跟继承父子类初始化非常的相似, 加载的时候首先加载的是父类,然后加载子类
如果父类加载成功. 父类认为是定义类, 包括父类以下都属于初始化类
为什么这样做,首先加载父类?
如果先加载的父类,那么我们的父类会约束我们的自定义 加载器,防止出现不安全的类被加载

举一个例子

在根加载器里面我们 有一个object类, 假如父加载器没有约束我们,那么这个时候我们自定义加载器来冒充object类,所以不安全, 假如我们可以根加载器可以约束, 那么他就不会被冒充,她会去父加载里面加载,避免了冒充

类对象什么时候被卸载了?

通过图我们可以看出当类对象没有系统类型加载器引用,也没有实例对象引用的时候,那么这个时候他就已经被卸载了
在这里插入图片描述

需要注意的是根加载器Bootstrap是不可能被卸载的,因为他会一直引用类对象, 所以类对象不会被卸载

什么是连接

类加载完成之后就是连接, 连接分为3个阶段

验证

验证主要是查看代码是否规范
文件架构检查

准备

静态变量分配内存
赋初始值
下面有代码

解析

1 把类符号引用转换为直接引用

Car.run 在二进制里面run就是一个类字符, 但是解析的时候就变成了直接引用,也就是指针

初始化

也就是为静态变量赋值

方法区里面存储了什么

类信息, 常量, 静态变量, 常量池,编译后的代码

在以前的是产量池是单独管理的,使用了和堆一样的gc回收,但是由于bug或者效果不收,所以没有使用,老年代的时候

代码介绍 ClassLoader

通过下面代码我们引入话题.

这段代码运行出的结果是什么?

   public class Test3 {
    public static void main(String[] args){
        t  test3 = t.getTest();
        System.out.println(test3.anInt);
        System.out.println(test3.anInt1);

    }
}
   class t {
        public static int anInt ;
        public static int anInt1 = 0;
       private static t test = new t();

        private t() {
            anInt++;
            anInt1++;
        }

        public static t getTest(){
            return test;
        }

}


1
1

如果是这样的,那么运行结果是什么样子的呢?

 class t {
        private static t test = new t();
        public static int anInt ;
        public static int anInt1 = 0;
    

        private t() {
            anInt++;
            anInt1++;
        }

        public static t getTest(){
            return test;
        }

1
0

为什么会出现这种情况呢?跟什么有关系

  1. 跟类加载器有关
  2. 程序执行顺序有关

编译常量

public class ClassLoaderTest2 {
 public static void main(String[] args){
     System.out.println(FinalTest2.x);

 }
}
class FinalTest2{
    public static final int x=18/3;
    static {
        System.out.println("this is judy");
    }
}

运行结果: 6 this is judy

public class ClassLoaderTest3 {
    public static void main(String[] args){
        System.out.println(FinalTest3.x);
    }
}
class FinalTest3{
    public static final int x= new Random().nextInt(100);
    static {
        System.out.println("this is judy");
    }
}

运行结果: this is judy 6

为什么会出现这种问题?

第二种方式的时候发现类被初始化了,他已经不属于编译时的常量了, 而第一种情况看是在编译的时候已经初始化赋值了,所以先执行的6后执行的this is judy

初始化

class Parent{
    static  int a=3;
    static {
        System.out.println("Parent2 static block");
    }
}

//父类已经被初始化过,所以不会再次被初始化
class Child extends Parent{
    static int b=4;
    static {
        System.out.println("child static block");
    }

}

public class ClassLoaderTest4 {
    static {
        System.out.println("Test5 static block");
    }
    public static void main(String[] args){
        //不是创建类的实例,不是主动使用
        Parent parent;
        System.out.println("--------------------------");
        parent = new Parent();
        System.out.println(Parent.a);
        System.out.println(Child.b);
    }
}

运行结果:
Test5 static block
#--------------------------
Parent2 static block
3
child static block
4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王雪芬-ghqr-264962

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值