JDK之JVM中Java对象的头部占多少byte

会有一股神秘感。 提交于 2020-03-01 12:28:31

    先做个铺垫:

  •         在32位机器上word size是32bits,CPU一次性处理32bits,在64位机器上word size是64bits,CPU一次性处理64bits。
  •         Data bus size, instruction size, address size are usually multiples of the word size,这句参考自Stackoverflow。   

1. Stackoverflow上看到的Java对象头部mark word和kclass pointer的大小

    从Stackoverflow上看到,Java对象头部有一个mark word和一个klass pointer,

  •     mark word:32bits architectures上,mark word占32bits,64bits architectures上,mark word占64bits;
  •     kclass pointer:32bits architectures上,kclass pointer占32bits,64bits architectures上,kclass pointer占64bits,但也可能是32bits,原话是这样"the klass pointer has word size on 32 bit architectures. On 64 bit architectures the klass pointer either has word size, but can also have 4 byte if the heap addresses can be encoded in these 4 bytes"。

2. 上面说的是否正确呢,我本地JVM上对象头部的mark word和kclass pointer也是如上述那样吗?

    我们来验证下。注意:我电脑上装的JDK1.8,64位的

2.1 验证Java对象的头部占用byte数

    如下:

    List-1

mjduan@mjduandeMacBook-Pro:/tmp % java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

     我用openJDK的jol来查看对象的layout,源码如下,直接运行main方法,

    List-2

package com.mjduan.project.openjdk_jol_example;

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

/**
 * @author dmj1161859184@126.com mjduan 2018-06-29 17:10
 * @version 1.0
 * @since 1.0
 */
public class JOLSample_12_ThinLocking {

    
    public static void main(String[] args) throws Exception {
        System.out.println(VM.current().details());

        final A a = new A();

        ClassLayout layout = ClassLayout.parseInstance(a);
    }

    public static class A {
        // no fields
    }
}

    List-2源码运行的结果如下图,

                                                        图1 List-2中main运行的结果

    图1中,第一个红框中的就是对象头部的mark word,占了8bytes,即64bits,这个也可以直接参考openJDK8的hotspot的markOop.hpp。之后4bytes,即第二个红框,是kclass pointer占的,即4bits。

图1中offset从12开始的4个bytes,没有被使用到。图1中对象头部的mark word和kclass pointer占了12bytes,但是最后JVM却认为它占了16bytes,为什么呢,这和内存的aligment有关,所以加了最后的4bytes,让总的byte数是8的倍数(这里的8表示8bytes,即64bits),为什么是64bits,因为我的机器是64位的,我的JVM是64位的。

2.2 验证Java数组对象的头部占用byte数

    List-3 验证数组的情况

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

/**
 * @author dmj1161859184@126.com mjduan 2018-06-29 13:16
 * @version 1.0
 * @since 1.0
 */
public class JOLSample_11_ClassWord {

    public static void main(String[] args) throws Exception {
        System.out.println(VM.current().details());

        A[] as = new A[2];
        System.out.println(ClassLayout.parseInstance(new A[2]).toPrintable());
    }

    public static class A {
        // no fields
    }
}

    List-3的运行结果如下图2

                                                           图2  List-3的运行结果

    图2的说明:第一个红框和第二个红框分别是mark word和kclass pointer,它们分别占8bytes和4bytes,之后的4bytes用来表示数组长度。第三个红框下面的8bytes是什么呢?List-3源码中的,我们的数组new A[2]的长度是2,每个下标处占4bytes,这个类似C语言中的指针。

    所以可以看到数组和普通的Java对象头部是有区别的。

3.分析Java的伪分享时考虑对象头部占的byte

    我们在做伪分享分析,进行填充数据时,要考虑对象头部,最好自己测试下自己系统JVM上对象头部占多少bytes,不要照搬别人的数据,因为很有可能别人使用的JVM和你的不一样。

    下面我们来分析俩种情况。

    List-4

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

/**
 * @author dmj1161859184@126.com mjduan 2018-06-29 12:34
 * @version 1.0
 * @since 1.0
 */
public class JOLSample_01_Basic {

    public static void main(String[] args) throws Exception {
        System.out.println(VM.current().details());
        ClassLayout layout = ClassLayout.parseInstance(new A());

        System.out.println(layout.toPrintable());
    }

    public static class A {
        boolean f;
    }
}

    List-4的运行结果下图3所示:

                                                           图3 List-4的运行结果

    List-5

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

/**
 * @author dmj1161859184@126.com mjduan 2018-06-29 12:36
 * @version 1.0
 * @since 1.0
 */
public class JOLSample_02_Alignment {

    public static void main(String[] args) throws Exception {
        System.out.println(VM.current().details());
        ClassLayout layout = ClassLayout.parseInstance(new A());
        System.out.println(layout.toPrintable());
    }

    public static class A {
        long f;
    }
}

    List-5的运行结果如下图4所示:

                                                           图4 List-5的运行结果

    我们来对比下图3和图4的结果,图3中显示对象占了16bytes,但是图4中显示对象占了24bytes。是什么导致结果变化的呢,注意看类A中的属性,由boolean类型变为了long,在long的情况下,占8bytes,不能使用12~15这4个bytes,所以给long类型的属性f分配的是offset从16开始的8个bytes。这是由于操作系统在内存管理方面的aliment导致的。

 

    经过上面的这么多分析,我们应该发现要想确定对象占有的byte数,还是难的。JVM中对象占用byte的情况除图3和图4外,还有其它情况的。所以不要轻易的照搬别人的数据,最好是自己测试下。

4.类中的get/set/contructor对对象占多少byte有影响吗

    一般情况下,Java类不仅有属性,也有方法。经过上面的实验,证实类属性对对象占多少byte有影响,那么类的方法数量是否多对象占byte有影响呢?我们来做实验验证下。

    我们给List-5中的类,加上get/set/constructor,如下List-6

    List-6 类A加上get/set/contructor

public class A {
    long f;

    public A() {
    }

    public long getF() {
        return f;
    }

    public void setF(long f) {
        this.f = f;
    }

    @Override
    public String toString() {
        return "A{" +
                "f=" + f +
                '}';
    }
}

    再用List-5的main方法代码,执行下,看结果:

                                                             图5 List-6的运行结果

    图5中的结果与图4中的结果一样,说明类的方法数量,对Java对象占多少byte没有影响。当然,这里我只是实验了一个,这个结论不是很严谨。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!