美文网首页
聊聊高并发(四)Java对象的表示模型和运行时内存表示

聊聊高并发(四)Java对象的表示模型和运行时内存表示

作者: wenming6688 | 来源:发表于2018-08-23 09:16 被阅读0次

    在继续了解Java内存模型之前,最好先理解Java对象的内存表示。在网上搜了下Java对象内存表示,说得都不够系统和到位。之前看了《Hotspot实战》一书,对JVM如何表示对象这块说得挺好,推荐一下。如果不理解JVM运行时的各种内存区域以及Java调用的过程,那么很难把Java内存模型理解到位。这个是一个比较大的主题,以后会陆续写一些JVM相关的。这里单把Java对象的内存拿出来聊聊,文中内容都基于Hotspot虚拟机。

    Hotspot主要是用C++写的,所以它定义的Java对象表示模型也是基于C++实现的。

    Java对象的表示模型叫做“OOP-Klass”二分模型,包括两部分:

    1. OOP,即Ordinary Object Point,普通对象指针。说实话这个名称挺难理解。说白了其实就是表示对象的实例信息

    2. Klass,即Java类的C++对等体,用来描述Java类,包含了元数据和方法信息

    一个Java对象就包括两部分,数据和方法,分别对应到OOP和Klass。最简单的理解就是如果让你自己用Java语言来开发一套新的语言,你如何来表示这个新的语言的对象呢。肯定也是类似的思路,一个模块是用Java类来实现表示数据的部分,一个模块是用Java类实现表示方法和元数据的部分。

    JVM运行时加载一个Class时,会在JVM内部创建一个instanceKlass对象,表示这个类的运行时元数据。创建一个这个Class的Java对象时,会在JVM内部相应的创建一个instanceOop来表示这个Java对象。熟悉JVM的同学可以明白,instanceKlass对象放在了方法区,instanceOop放在了堆,instanceOop的引用放在了JVM栈。

    JVM是基于栈来运行的,当一个线程调用一个对象的方法时,会在它的JVM栈的栈顶创建一个栈帧(Frame)的数据结构,这个数据结构是用来保存方法的局部变量,操作数栈,动态连接和方法返回值的。通过参数传递的值和在方法中new出来的对象的引用都保持在局部变量表里面。

    Java的方法调用是值传递,不是引用传递,原因就在这里,传递进来的参数相当于在局部变量表里面拷贝了一份,实际计算时,操作数栈操作的是局部变量变量里面的值,而不是外部的变量。

    图片.png

    在堆中创建的Java对象实际只包含数据信息,它包含三部分:

    1. 对象头,也叫Mark Word

    2. 元数据指针,可以理解为类对象指针,指向方法区的instanceKlass实例

    3. 实例数据

    如果是数据对象的话,还多了一个部分,就是数组长度。

    图片.png

    对象头主要存储对象运行时记录信息,如hashcode, GC分代年龄,锁状态标志,偏向线程ID,偏向时间戳等。对象头的长度和JVM的字长一致,比如32位JVM的对象头是32位,64位JVM的对象头是64位。

    这里可以看到,所谓的给一个对象加锁,其实就是设置了对象头某些位。当其他线程看到这个对象的状态是加锁状态后,就等待释放锁。

    在方法区的instanceKlass对象相当于Class加载后创建的运行时对象,它包含了运行时常量池,字段,方法等元数据,当调用一个对象的方法时,如上面的图所示,实际定位到了方法区的instanceKlass对象的方法元数据。

    下面我们通过一个实例,使用HSDB来看看运行时的instanceKlass和instanceOop到底是什么样的。在方法区

    创建一个Person类,有name, age, sex实例属性,有一个sayHi方法

    package main; public class Person {    private String name;    private int age;    private boolean sex;        public void sayHi(){        System.out.println("Say hi from ITer_ZC");    }        public static void main(String[] args){        Person p = new Person();        p.sayHi();                try {            Thread.sleep(500000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }} 
    

    HSDB是一款内置与SA的GUI调试工具,集成了各种JVM监控工具,可以用来深入分析JVM内部状态。

    启动HSDB:

    java -cp ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    

    运行Person,使用JPS查看进程ID

    图片.png

    使用HSDB attach Person进程

    图片.png

    Attach成功后看到21461进程里的各个子进程

    图片.png

    在Class Browser里面找到Person对应的instanceKlass的运行时实例

    图片.png

    在inspector里面查看0x000000077fc82328这个对象实例,我们可以看到instanceKlass的字段,方法,运行时常量池,父类,兄弟类等元数据信息

    图片.png

    在Object Histogram里面找到main.Person对象

    图片.png

    查看main.Person Oop的运行时实例

    图片.png

    _mark就是对象头,接着是实例数据信息。HSDB没有显示类对象指针

    图片.png

    相关文章

      网友评论

          本文标题:聊聊高并发(四)Java对象的表示模型和运行时内存表示

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