3.JVM虚拟机-对象内存布局,访问定位

目录

1. 布局工具使用

1.1 JOL(java object layout)添加:

1.2 JOL使用

1.3 打印内存局部实例

2. 对象内存布局

2.1 对象内存布局详解

2.1 对象头信息

3.对象访问定位

3.1 句柄访问 

3.2 直接指针访问


 

1. 布局工具使用

1.1 JOL(java object layout)添加:

此jar包是为了打印对象内存布局使用。

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>put-the-version-here</version>
</dependency>

 

1.2 JOL使用

运行如下代码:

public class JavaObjectLayout {
    public static void main(String[] args) {
        /*打印虚拟机信息*/
        System.out.println(VM.current().details());
    }
}

details方法的源码中信息如下:

out.printf("# %-19s: %d, %d, %d, %d, %d, %d, %d, %d, %d [bytes]%n", "Field sizes by type", this.oopSize, this.sizes.booleanSize, this.sizes.byteSize, this.sizes.charSize, this.sizes.shortSize, this.sizes.intSize, this.sizes.floatSize, this.sizes.longSize, this.sizes.doubleSize);
out.printf("# %-19s: %d, %d, %d, %d, %d, %d, %d, %d, %d [bytes]%n", "Array element sizes", this.U.arrayIndexScale(Object[].class), this.U.arrayIndexScale(boolean[].class), this.U.arrayIndexScale(byte[].class), this.U.arrayIndexScale(char[].class), this.U.arrayIndexScale(short[].class), this.U.arrayIndexScale(int[].class), this.U.arrayIndexScale(float[].class), this.U.arrayIndexScale(long[].class), this.U.arrayIndexScale(double[].class));
        

 

运行结果为:打印的为此虚拟机基本数据类型与引用类型所占字节大小

"C:\Program Files\Java\jdk1.8.0_101\bin\java.exe"
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Field sizes by type(Field占用字节数):引用句柄, boolean,byte, char, short, int, float, long, double的长度

Array element sizes(数组占用字节数): 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]-数组类型的大小

 

1.3 打印内存局部实例

运行如下代码:

        Object object=new Object();
        System.out.println(ClassLayout.parseInstance(object).toPrintable());

        Object[] objects=new Object[10];
        System.out.println(ClassLayout.parseInstance(objects).toPrintable());

上述代码创建一个Object对象和长度为10的数组对象,并且使用ClassLayout打印出此对象的内存布局,运行结果如下:

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[Ljava.lang.Object; object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           f2 22 00 f8 (11110010 00100010 00000000 11111000) (-134208782)
     12     4                    (object header)                           0a 00 00 00 (00001010 00000000 00000000 00000000) (10)
     16    40   java.lang.Object Object;.<elements>                        N/A
Instance size: 56 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

从结果中可以看出:

Instance size: 16 bytes 代表此object对象占用的内存大小为16byte,那16byte是什么呢?

Instance size: 56 bytes 代表此数组对象占用的内存为56bytes.

 


2. 对象内存布局

2.1 对象内存布局详解

下图是java普通对象与数组对象的内存布局图:

对象在内存中存储的布局分为三块区域:对象头实例数据对其填充

1. markword

          markword 固定长度8byte,描述对象的identityhashcode,分代年龄,锁信息等

2. classpoint

           classpoint固定长度4byte, 指定该对象的class类对象。(JVM默认使用-XX:+UseCompressedClassPointers 参数进行压缩。如不使用可设置-XX:-UseCompressedClassPointers关闭,则该字段在64位jvm下占用8个字节;  可使用java -XX:+PrintCommandLineFlags -version 命令查看默认的或已设置的jvm参数)

3 . 实例数据

          基本变量:用于存放java八种基本类型成员变量,以4byte步长进行补齐,使用内存重排序优化空间

          引用变量:存放对象地址,如String,Object;占用4个字节,64位jvm上默认使用-XX:+UseCompressedOops进行压缩,                                      可使用-XX:-UseCompressedOops进行关闭,则在64位jvm上会占用8个字节;

4. padding 补齐

           对象大小必须是8byte的整数倍,用来补齐字节数。Object o = new Object() 在内存中占用16个字节,其中最后4个是补              齐;至于为什么要补齐,在以后的学习中会进行讲解

5. 数组长度

          数组长度:如果是数组,额外占用固定4byte存放数组长度;

 

在1.3章节中的实例,运行结果说明

Instance size: 16 bytes:markword(8 byte)+classpoint(4 byte)+padding(4 byte)

Instance size: 56 bytes:   markword(8 byte)+classpoint(4 byte)+arrayLength(4 byte)+引用句柄(4*10)

 

2.1 对象头信息

1. 对象头分为两部分,一部分存储对象自身运行时数据,例如HashCode,GC分代年龄,锁状态等。

2. 另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定对象所属实例。但是并不是所有的对象数据保留类型指针,换句话说查找对象的元数据信息并不一定经过对象本身

3. 如果对象是一个java数组,对象头中必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通java对象的元数据信息确定java对象的大小,但是从数据的元数据中无法确定数组的大小

下图是64位虚拟机对象头,markword的信息:这其中包含锁升级的过程,在以后讲锁升级时会进行讲解。


3.对象访问定位

 

说明:java程序通过栈上的reference数据来操作堆上的具体对象,目前主流的访问对象方式又两种,使用句柄直接指针两种

3.1 句柄访问 

用句柄的方式,在java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄,而句柄中包含对象实例数据类型数据各自的具体地址信息。好处时reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要修改。

 

3.2 直接指针访问

使用直接指针方式访问,java堆对象的布局中就必须考虑如何防止访问类型数据的相关信息,而reference中存储的直接就是对象地址好处是速度快,节省了一次指针定位的时间开销hotspot基于此实现

 

参考资料:

1. 《深入理解Java虚拟机》2.3章节

2.  博客:https://www.jianshu.com/p/91e398d5d17c

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值