飞道的博客

new对象时Java虚拟机的工作流程解析

378人阅读  评论(0)

周日的上午,笔者像往常一样盯着电脑发呆,想起昨晚的梦中,一个身着西装的面试官问道:你知道new一个对象时,Java虚拟机是怎么工作的吗?
我:

没办法,那今天就复习一下好了。

类的生命周期分为7个阶段加载、验证、准备、解析、初始化、使用、卸载

下图是笔者自定义的类。

class man {
   
    String s;
    public man(String string){
   
        s = string;
    }
}

public class Solution{
   
    public void test(){
   
        man a = new man("abc");
        return;
    }
}

利用jclasslib工具可以看到test方法的字节码文件。

 0 new #7 <test/man>
 3 dup
 4 ldc #9 <abc>
 6 invokespecial #11 <test/man.<init>>
 9 astore_1
10 return

下面结合字节码文件对该类的生命周期进行分析。

加载:加载阶段发生在第一步之前,主要作用有:

  • 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有:

    • _java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用

    • _super 即父类

    • _fields 即成员变量

    • _methods 即方法

    • _constants 即常量池

    • _class_loader 即类加载器

    • _vtable 虚方法表

    • _itable 接口方法表

  • 中生成一个代表这个类的java.lang.Class对象(比如样例中即为man.Class对象),作为方法区这个类的各种数据的访问入口。

验证:验证阶段同样发生在第一步之前,主要作用为验证类是否符合 JVM规范,安全性检查。(注意验证阶段往往和加载阶段同时进行)

准备:当一个类验证通过时,虚拟机就会进入准备阶段。在这个阶段,虚拟机就会为这个类分配相应的内存空间,并设置初始化值。

解析:将常量池中的类、接口、字段、方法符号引用转为直接引用,比如类中定义了一个方法,该方法一开始作为符号引用存储于常量池中,解析后可在常量池中直接定位到该方法在内存中的真实地址。(注意:《Java虚拟机规范》并未规定解析阶段发生的具体时间,只要求在invokespecial等17个用于操作符号引用的字节码指令之前,对它们使用的符号引用进行解析,因此,解析有时发生在初始化阶段之后

注意:验证准备解析三个阶段统称为链接阶段,其中验证阶段往往与加载阶段同时进行。

之后正式进入初始化阶段,并开始执行字节码文件中的内容。

初始化:是类加载过程的最后一个步骤,直到初始化阶段,Java虚拟机才真正开始执行类中编写的程序代码

下面从字节码文件入手,分析整个过程。

首先是

new #7 <test/man>

在该线程的虚拟机栈的操作数栈压入了指向之前分配的man的实例的内存的地址供任何下面的操作来调用。

dup

复制操作数栈的栈顶值,并将其压入栈顶,也就是说此时操作数栈上有连续相同的两个对象地址

ldc #9 <abc>

将String型常量值"abc"从常量池中推送至栈顶。

//在该条字节码指令之前解析常量池中的#11即可
invokespecial #11 <test/man.<init>>

调用类的构造方法。(注意:这里将之前压入操作数栈的"abc"取出来作为实参传入了类的构造方法,接着从操作数栈顶弹出一个实例对象的引用,即将对象创建在之前入栈的对象地址上)

astore_1

从操作数栈顶取出 man 对象的引用并存到局部变量表。

return

最后由return指令结束方法。

以上便是全部的类加载过程,如有纰漏,欢迎指正。


转载:https://blog.csdn.net/qq_44998067/article/details/115590869
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场