概述

Java对象的内存布局如下,以下hotspot源码都是基于openjdk 1.8

1个字宽大小取决于操作系统,32位操作系统1个字宽为4个字节,64位则为8个字节

  • 对象头(2或者3个字宽)
  • 1个字宽:Mark Word
  • 1个字宽:Class pointer
  • 1个字宽:数组长度(如果是数组对象的话)
  • 实例域

N个字宽,取决于成员变量

 

openjdk 1.8 对象头源码如下

//hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
  friend class VMStructs;
 private:
  //Mark Word
  volatile markOop  _mark;
  //Class poniter
  union _metadata {
    //普通指针
    Klass*      _klass;
    //压缩指针
    narrowKlass _compressed_klass;
  } _metadata;
}

 

对象头

Mark Word

在hotspot/src/share/vm/oops/markOop.hpp

 

//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

 

其中lock的两个标志位如下

  • 01无锁
  • 00轻量锁
  • 10重量锁
  • 11GC标记

 

指针压缩

在32位里面采用的指针大小为4个字节,但是在64位的操作系统下面如果堆内存不超过32G,就开启指针压缩功能,可以使用32位的指针表示32G的地址(原本32位的指针只能表示4G的地址)

 

原因

因为64位的vm都是以8个字节对齐的(1字宽),意味着在java中所有的对象地址都是8的倍数,即低3位都是000,所以可以重用这三位,做一个映射:真实地址 = 压缩地址<<3,所以32位原本只能表示4G的地址就可以增加到32G

 

-XX:+/-UseCompressedOops参数可以控制是否开启指针压缩

 

实例域

 

控制实例域布局的参数-XX:FieldsAllocationStyle=mode,mode可以是0,1,2

在Java中数据类型中分为两种

  • 基本数据类型
  • long / double - 8 bytes
  • int / float - 4 bytes
  • short / char - 2 bytes
  • byte/boolean - 1 bytes
  • 引用类型
  • reference type - 4 or 8 bytes

 

为了方便观看可以使用openjdk提供的jol工具,对一下Son类进行打印

 

    public static class Son extends Father {
        Object filed1;
        char field2;
        short field3;
        Object filed4;
        long field5;
        byte field6;
        double filed7;

    }

    public static class Father{
        Object filed1;
        char field2;
        short field3;
        byte field4;
        Object filed5;
    }

 

在布局的过程中,对实例域的布局遵循尽可能适合的方式以减少间隙,因为由于填充会形成gap空洞, 比如使用压缩指针时, 头占12字节, 后面如果是long的话, long的对齐要求是8字节, 中间会有4个字节的空洞, 为了高效利用, 可以把int/short/byte等比较小的对象塞进去, 与此同时JVM提供了开关控制该特性-XX:+/-CompactFields, 默认开启.

FieldsAllocationStyle=0

这种布局模式是先排父类再到子类,并且引用类型在前面,基本数据类型在后面

 

 

使用jol打印的结果如下

 

 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)                           82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
     12     4   java.lang.Object Father.filed1                             null
     16     4   java.lang.Object Father.filed5                             null
     20     2               char Father.field2                              
     22     2              short Father.field3                             0
     24     1               byte Father.field4                             0
     25     3                    (alignment/padding gap)                  
     28     4   java.lang.Object Son.filed1                                null
     32     4   java.lang.Object Son.filed4                                null
     36     2               char Son.field2                                 
     38     2              short Son.field3                                0
     40     8               long Son.field5                                0
     48     8             double Son.filed7                                0.0
     56     1               byte Son.field6                                0
     57     7                    (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 3 bytes internal + 7 bytes external = 10 bytes total

 

FieldsAllocationStyle=1(默认)

这种布局模式是先排父类再到子类,并且基本数据型在前面,引用类型在后面

 

 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)                           82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
     12     2               char Father.field2                              
     14     2              short Father.field3                             0
     16     1               byte Father.field4                             0
     17     3                    (alignment/padding gap)                  
     20     4   java.lang.Object Father.filed1                             null
     24     4   java.lang.Object Father.filed5                             null
     28     2               char Son.field2                                 
     30     2              short Son.field3                                0
     32     8               long Son.field5                                0
     40     8             double Son.filed7                                0.0
     48     1               byte Son.field6                                0
     49     3                    (alignment/padding gap)                  
     52     4   java.lang.Object Son.filed1                                null
     56     4   java.lang.Object Son.filed4                                null
     60     4                    (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 6 bytes internal + 4 bytes external = 10 bytes total

 

FieldsAllocationStyle=2

这种布局模式是先排父类与子类的引用类型相靠近

 

 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)                           82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190)
     12     2               char Father.field2                              
     14     2              short Father.field3                             0
     16     1               byte Father.field4                             0
     17     3                    (alignment/padding gap)                  
     20     4   java.lang.Object Father.filed1                             null
     24     4   java.lang.Object Father.filed5                             null
     28     4   java.lang.Object Son.filed1                                null
     32     4   java.lang.Object Son.filed4                                null
     36     2               char Son.field2                                 
     38     2              short Son.field3                                0
     40     8               long Son.field5                                0
     48     8             double Son.filed7                                0.0
     56     1               byte Son.field6                                0
     57     7                    (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 3 bytes internal + 7 bytes external = 10 bytes total

 

中间的20~32就是所有的引用类型

OopMapBlock 

这个数据结构代码如下hotspot/src/share/vm/oops/instanceKlass.hpp

 

class OopMapBlock VALUE_OBJ_CLASS_SPEC {
//省略
 private:
  int  _offset;
  uint _count;
};

 

这个互数据结构是用来记录这个类的域的偏移量和数量,目的是为了GC扫描存活对象的时候访问该类的引用对象,完成可达性分析

 

在上面3中域的布局中,0,1都需要2个OopMapBlock,而2只需要一个,因为对象里面的引用类型都是连续的

java开发实习工程师http://www.gtalent.cn/exam/interview/jdAgwJtErNal1znK