美文网首页
JVM扩展(1):试图讲清楚 类的加载与对象的创建

JVM扩展(1):试图讲清楚 类的加载与对象的创建

作者: _River_ | 来源:发表于2021-04-28 22:15 被阅读0次
1:类与对象
类是抽象的概念  对象是具体的概念
类的实例就是对象   
想要有对象 当然必须要有类

如何获取类?
通过类加载的方式  从字节码文件中获取 类

如何获取对象?
通过创建该类的创建对象方法(我们所说的new方法)    生成一个对象

类应该有什么东西?
1:什么时候加载类?
2:通过什么加载类?
3:加载好的类放在哪里?
4:类里面有什么?

对象应该有什么东西?
1:什么时候创建    类的对象
2:通过什么方式创建   类的对象
3:创建好的  类的对象放在哪里
4:对象里面有什么?
2:Java源代码经过 编译解释成二进制文件 流程
1:Java源代码 
2:经过JDK中Javac 编译
3:字节码文件(.class)
4:JVM 类加载器     进行加载字节码文件

5:方法:解释:
     1:解释器      逐行解释执行
     2:机器可执行的二进制机器码

5:方法:编译:
     后续引进:JIT即时编译器(Just In Time Compiler)
     处理热点代码(一次编译 多次执行)
     2:JIT      运行时进行编译
     3:机器可执行的二进制机器码
     4:编译完成后机器码保留
     5:机器码下次可以直接使用
3:流程图中类加载(包括类初始化) 流程
加载类解读
其中loadClass的类加载过程有如下几步:
加载 >> 验证 >> 准备 >> 解析 >> 初始化

1:加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,
        例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,
        作为方法区这个类的各种数据的访问入口

2:验证:校验字节码文件的正确性

3:准备:给类的静态变量分配内存,并赋予默认值 (如整型为0 布尔类型为false)(注意final 是常量 直接赋值)

4:解析:将符号引用 (符号 符号 符号)   替换为     直接引用(内存地址)
    静态链接过程(类加载期间完成):该阶段会把一些静态方法(符号引用,比如main()方法)
                                           替换为指向数据所存内存的指针或句柄等(直接引用),
    动态链接过程(程序运行期间完成):将符号引用替换为直接引用,下节课会讲到动态链接。

5:初始化(加载到方法区):对类的静态变量初始化为指定的值,执行静态代码块被加载到方法区中
                                   主要包含 运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息

类加载器的加载(懒加载)(类创建才执行):ClassLoader classLoad =new ClassLoader()
                                                          类加载器实例        加载       这个类
                                                          classLoad           加载     User.CLass

如何获取方法区里面的类信息:类加载器在加载类信息放到方法区中后,
                                    会有一个对应的Class 类型的对象实例放到堆(Heap)中, 
                                    作为开发人员访问方法区中类定义的入口和切入点。         

类对象创建时 栈  堆  方法区 的关系:
User user = new User()

    1:类加载完后,其类信息存放在方法区中,并在堆中提供方法区入口 UserClass
    2:堆中申请  user(真实的对象)的内存空间 ,它指向方法区的User类型信息的引用;
        可以认为是根据方法区入口UserClass   生成堆中的user(真实的对象)
    
    3:栈中生成user(地址)(就是变量名user)
    4:栈中有user(地址)(就是变量名user)指向堆中的user(真实的对象)
   
    5:这样 栈中user地址  方法区中User类信息  堆中user真实对象  就完成串联在一起了

特别注意:
1:加载   >>  验证 >> 准备 >> 解析 >> 初始化   
   类加载                                       类初始化       
      
2:类对象创建 (class实例 的引用)
    一个对应的Class 类型的对象实例 是指 user (User user  =  new User() )    
    
3:加载的时机:
类的懒加载:第一次创建该类的对象时进行类加载,假如该类被加载过,则不需要重新加载。

那么执行顺序就有意思了
1:创建类的对象  >>  类未加载  >>  类加载器进行加载 >> 加载  >> 验证 >> 准备 >> 解析 >> 初始化  >>    类对象创建 
2:创建类的对象 >>   类已加载   >>   类对象创建    
4:类对象创建(包括对象初始化) 流程
类的实例化也叫对象初始化,这是一个概念。类是对象的抽象,对象是类的具体实例!

1:虚拟机遇到一条new指令时,会先检查相应的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

2:分配给新生的对象分配内存 即是申请内存空间

3:虚拟机需要将分配到的内存空间都初始化为零值
4:设置对象头(先了解)
5:执行<init>方法 ,即对象按照程序员的意愿进行初始化,对应到语言层面上讲,就是为属性赋值
5:问题回答
 类应该有什么东西?
1:什么时候加载类?
    类使用懒加载的方式 即是创建该类的对象时 才进行加载
    
2:通过什么加载类?
    项目启动后 项目有个类加载器(实际上就是JVM自带的一个类) 来进行加载

3:加载好的类放在哪里?
    加载的类信息会放在JVM的方法区

对象应该有什么东西?
1:什么时候创建    类的对象
    当你需要使用该对象时创建该对象
    
2:通过什么方式创建   类的对象
    通过new的方式创建对象

3:创建好的  类的对象放在哪里
    new一个对象时  会在JVM的堆中申请内存空间 然后该对象当然放在堆中 

4:类里面有什么?
4:对象里面有什么?

类是抽象概念   对象时具体概念  相应的类里面有什么  对象里面也就有什么

如果对 加载类 和 创建类的对象 的时机  还是不理解没关系 看下面打印结果
打印结果:

    load TestDynamicLoad
    
    静态方法执行了一次
    load ClassLoaderA
    
    构造方法执行了两次
    initial ClassLoaderA
    initial ClassLoaderA
    
    load test
public class TestDynamicClassLoader {

    static {
        System.out.println("*************load TestDynamicLoad************");
    }

    public static void main(String[] args) {
        //第一次创建对象 (进行类加载)
        new ClassLoaderA();
        //第二次创建对象 (不进行类加载)
        new ClassLoaderA();
        System.out.println("*************load test************");
        //B不会加载,除非这里执行 new B()
        ClassLoaderB b = null;
    }
}
public class ClassLoaderA {
    static {
        System.out.println("*************load ClassLoaderA************");
    }

    public ClassLoaderA() {
        System.out.println("*************initial ClassLoaderA************");
    }
}
public class ClassLoaderB {
    static {
        System.out.println("*************load ClassLoaderB************");
    }

    public ClassLoaderB() {
        System.out.println("*************initial ClassLoaderB************");
    }
}
6:User user = new User( ) 做了什么
public class User {
    private String name = "章鱼";
    private int    age  = 18;

    public User() {
        name = "何穗金";
        age = 18;
    }
}

class UserDemo {
    public static void main(String[] args) {
        User user = new User();
    }
}

  注意:
    1:堆中 user真实对象  是根据方法区中User的类信息创建的
    2:然后才是 栈中user的地址 指向堆中的user真实对象

 1:用户创建了一个User对象,运行时JVM首先会去方法区寻找该对象的类型信息,
     没有则使用类加载器classloader将Userclass字节码文件加载至内存中的方法区,
     并将Student类的类型信息存放至方法区。
    
 2:接着JVM在堆中为新的User实例分配内存空间,
     这个实例持有着指向方法区的User类型信息的引用,引用指的是类型信息在方法区中的内存地址。

 3:在此运行的JVM进程中,会首先起一个线程跑该用户程序,而创建线程的同时也创建了一个线程栈,
     线程栈用来跟踪线程运行中的一系列方法调用的过程,每调用一个方法就会创建并往栈中压入一个栈帧,
     栈帧用来存储方法的参数,局部变量和运算过程的临时数据。
     上面程序中的user是对User的引用,就存放于栈中,并持有指向堆中User实例的内存地址。

 4:JVM根据user引用持有的堆中对象的内存地址,定位到堆中的User实例,
     由于堆中实例持有指向方法区的User类型信息的引用,
     因此也可以获取到 该类在方法区的其他信息(常量 静态变量 类信息)

 对象就是一种变量
 
1、把User. class文件加载到内存
2、在栈内存给user对象(变量)申请一个空间
3、在堆内存为user对象(变量)申请一个空间

4、给成员变量进行默认初始化。name=null, age =0

5、给成员变量进行显示初始化。name=章鱼,age=18

6、通过构造方法给成员变量进行初始化。name=何穗金,age=18

7、数据初始化完毕,然后把堆内存的地址值赋值给栈内存的 user变量。

相关文章

网友评论

      本文标题:JVM扩展(1):试图讲清楚 类的加载与对象的创建

      本文链接:https://www.haomeiwen.com/subject/dqksrltx.html