先做个铺垫:
从Stackoverflow上看到,Java对象头部有一个mark word和一个klass pointer,
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
"。我们来验证下。注意:我电脑上装的JDK1.8,64位的
如下:
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位的。
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对象头部是有区别的。
我们在做伪分享分析,进行填充数据时,要考虑对象头部,最好自己测试下自己系统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外,还有其它情况的。所以不要轻易的照搬别人的数据,最好是自己测试下。
一般情况下,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没有影响。当然,这里我只是实验了一个,这个结论不是很严谨。
(adsbygoogle = window.adsbygoogle || []).push({});