ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

一个类的奇幻旅程 —— 类的加载与对象内存布局

2021-08-02 21:01:38  阅读:174  来源: 互联网

标签:name 对象 奇幻 String 实例 内存 JVM speak 加载


最近在学习JVM和Java高级特性,有了一些感悟,在此总结,如果有不对的地方,希望大家指出。

假设我们编写了一个java类,代码如下。

public class Student
{
    static final String schoolName = "中南林业科技大学";
    public String name;

    private Student(String name)
    {
        this.name = name;
    }

    public void speak()
    {
        System.out.println("我叫" + name + ",就读于" + schoolName);
    }

    public static void main(String[] args) 
    {
        String name = new String("黄超");
        Student student = new Student(name);
        student.speak();
    }
}

编写完成后,打开项目文件夹,我们可以看到一个名为 student.java 的文件。这就是我们编写的源文件。

现在我们执行打开 cmd ,执行 javac 命令,将它编译为一个字节码文件,这样就获得了一个名为 student.class 的文件。

接下来,我们开始执行这个类。
当我们输入执行命令后,Java虚拟机首先会加载这个类,为此JVM会自顶向下选用适当的类加载器。由于这个类是我们自己编写的,最后JVM会使用application加载器。

在加载的过程中,JVM按照类名,会读取磁盘上的 Student.class ,它是一个二进制字节流(Class字节码)。JvM会将读取的内容构建为一个class对象,也就是Student这个类的模板,然后将它放入方法区。

方法区在不同的jdk版本中,处于内存的不同的位置,对于jdk8而言,它位于Java堆的元空间中,之前这个位置被称为永生区。

加载完成后,还有链接初始化两个步骤。在链接阶段JVM将验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求。同时,会将静态变量 schoolName 放入方法区的常量池。在初始化阶段,JVM会执行模板类中的静态代码块,由于我们这个类没有,所以不会执行。

完成这一切之后,就开始正式执行了。
首先是执行主函数,JVM会将主函数放入Java虚拟机栈的栈底,为它分配一个栈帧。栈帧中存放了它的方法名、参数、运行时的临时变量。同时还有一个指向方法区中函数实体的引用。

然后就是逐行执行函数。

当函数执行到第三行时,在这里我们用 new 关键字,创建了一个 String 类型的对象 name 。

这时,JVM首先会实例化一个 String 对象放入空间的常量池,它的值为 “黄超” 。

然后虚拟机会在 main 的栈帧中选择一个页面,存放一个 String 类型的引用。
同时在堆中开辟一片区域,用于存放这个对象的实例。
对象的实例会首先被放在伊甸园区,经历一轮GC它后进入幸存区,经历15轮GC后会进入老年区。但这都是后话,在我们这个程序中,它因该是不会进入老年区的。
现在我们来分析以下对象实例在堆中伊甸园区是怎么组织的。它包含三个部分对象头实例数据对齐填充
对象头主要包括两类信息:第一类被称为Mark Word,是用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID、偏向时间戳等。第二类是类型指针,用于确定对象是哪个类的实例,即指向方法区的类模板。
实例数据可以认为就是“黄超”这个字符串本身。
对其填充则是为了起占位作用,HotSpot 虚拟机要求对象的起始地址必须是8的整数倍,因此需要对齐填充。

将对象实例放入堆后,JVM会将栈中的对象引用指向堆中的实例。

随后继续执行下一行。

在这里,我们创建了一个 Student 对象,与之前创建 String 对象的过程大致相同。但要注意,这一次,堆空间中 name 存放的是一个引用,而不是一个数据。这个引用指向的正是我们之前创建的那个字符串对象,也就是作为构造函数参数传入的 name 对象。

继续, main 将会调用 student 对象的 speak 方法。它通过对象的引用,访问到堆中的对象实例,然后通过对象实例中的类型信息找到它的模板类,也就是在最开始,JVM放入方法区的那个Class类。最后它会在方法区中找到 speak 方法。
JVM随后就会将找到的这个方法压栈。
执行 speak 方法,会向控制台打印一串字符串,然后结束,退栈。之后 main 方法也会结束,退栈。这会导致Java虚拟机栈变为空,即程序执行完毕。

但是我们先不着急执行完程序,在它打印之前,也就是 speak 方法刚完成入栈的那一刻,我们来看看JVM的内存是什么情况。

内存分布情况
虽然有些杂乱,但这正和我们之前分析的整个程序执行过程的一样。
最后,speak 函数执行,控制台打印出一串字符串。随后结束程序。
看完这里,一个类的旅程也就差不多结束了。

标签:name,对象,奇幻,String,实例,内存,JVM,speak,加载
来源: https://blog.csdn.net/weixin_44627151/article/details/119331633

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有