先做个铺垫:
- 在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. On64 bit
architectures the klass pointer either has word size, but can also have4 byte
if the heap addresses can be encoded in these4 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没有影响。当然,这里我只是实验了一个,这个结论不是很严谨。
来源:oschina
链接:https://my.oschina.net/u/2518341/blog/1838006