接着上一篇,我们继续来讲oopDesc相关的子类。

3、instanceOopDesc类

instanceOopDesc类的实例表示除数组对象外的其它对象。在HotSpot中,对象在内存中存储的布局可以分为三块区域:对象头(header)、对象字段数据(field data)和对齐填充(padding),如下图所示。

下面详细介绍一下这3个组成部分。

1.对象头

可以看到对象头分为两个部分,一个就是“Mark Word”,另外还有存储指向方法区对象类型数据的指针_klass或_compressed_klass。这两个都在介绍oopDesc类时详细介绍过,这里不再介绍。

2对象字段数据

对象字段数据存储Java源代码中定义的各种类型字段内容,具体包括父类继承及子类定义的对象字段。

存储顺序受到HotSpot分配策略参数(FieldAllocationStyle)和字段在Java源代码中定义顺序的影响。默认分配策略为:long/double、int、short/char、boolean、oops(对象指针,32位系统占用4字节,64位系统占用8字节),可以看到,相同宽度的字段总被分配到一起。

如果虚拟机的-XX:+CompactFields参数为true,子类中较窄的变量可能插入到父类变量空隙中,以压缩节省空间。例如,当碰到long/doubles时,会将一些短类型插入long/doubles和header的空隙中。(空隙:64位系统开启压缩指针,header占12个字节,剩下的4个字节就是空隙。更多字段存储顺序的内容将在第XX章详细介绍。

3对齐填充部分

对齐填充部分不是必须的,只起占位符作用,没有其他含义。HotSpot虚拟机要求对象大小必须是8字节的整数倍,对象头是8字节整数倍,所以填充是对实例数据没有对齐的情况来说的。

在创建instanceOop对象时会调用allocate_instance()方法,这个方法的实现如下:

instanceOop InstanceKlass::allocate_instance(TRAPS) {

  int size = size_helper();  // Query before forming handle.

  KlassHandle h_k(THREAD, this);

  instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
return i;
}

调用instanceKlass中的size_helper()方法获取创建instanceOop对象所需要的内存大小,调用CollectedHeap::obj_allocate()方法分配size大小的内存。首先介绍size_helper()方法的实现,如下:

// Use this to return the size of an instance in heap words:
int size_helper() const {
return layout_helper_to_size_helper(layout_helper());
} int layout_helper() const {
return _layout_helper;
} static int layout_helper_to_size_helper(jint lh) {
assert(lh > (jint)_lh_neutral_value, "must be instance");
// Note that the following expression discards _lh_instance_slow_path_bit.
return lh >> LogHeapWordSize;
}

从_layout_helper属性中获取大小,之前介绍过这个综合描述符,如果为InstanceKlass,则组合数字中含有的是instanceOop对象的大小,在设置时调用的是instance_layout_helper()方法,如下:

static jint instance_layout_helper(jint size, bool slow_path_flag) {
return (size << LogHeapWordSize) // LogHeapWordSize=3
| (slow_path_flag ? _lh_instance_slow_path_bit : 0); // 实例慢速分配有关
}

获取size时需要向右移动3位即可。这个方法在创建InstanceKlass对象时会调用,不过size通常会初始化为0,在调用parseClassFile()方法计算完实例的大小时,还会调用此方法更新为真正需要的instanceOop对象大小,在解析类文件时会详细介绍实例大小的计算过程。 

在InstanceKlass::allocate_instance()方法中调用CollectedHeap::obj_allocate()方法分配size大小的内存并将内存初始化为零值,方法将会在介绍垃圾回收时详细介绍,这里不介绍。

4、arrayOopDesc类

arrayOopDesc类的实例表示Java数组对象。具体的基本类型数组(对象)或对象类型数组(对象)由具体的C++中定义的子类实例来表示。在HotSpot虚拟机中,数组对象在内存中存储的布局可以分为三块区域:对象头(header)、对象字段数据(field data)和对齐填充(padding),如下图所示。

与Java对象内存布局唯一不同之处在于,数组对象的对象头中还会存储数组的长度length,占用的内存空间为4字节。在64位系统下,存放_metadata的空间大小是8字节,_mark是8字节,length是4字节,对象头为20字节,由于要按8字节对齐,所以会填充4字节,最终占用24字节。64位开启指针压缩的情况下,存放_metadata的空间大小是4字节,_mark是8字节,length是4字节,对象头为16字节。

5、arrayOopDesc类的子类

typeArrayOopDesc类的实例表示Java基本类型数组(对象),objArrayOopDesc类的实例表示Java对象类型数组(对象)。当需要创建typeArrayOopDesc对象时,通常会调用oopFactory类中定义的工厂方法,例如创建一个boolean数组,则调用new_boolArray()方法,如下:

static typeArrayOop    new_boolArray(int length, TRAPS) {
TypeArrayKlass* tak = TypeArrayKlass::cast(Universe::boolArrayKlassObj());
return tak->allocate(length, CHECK_NULL);
}

调用Universe::boolArrayKlassObj()方法获取_charArrayKlassObj属性的值,也就是之前介绍的、调用TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK)方法创建的表示boolean数组的TypeArrayKlass对象,然后调用TypeArrayKlass中的allocate()方法创建typeArrayOop对象,如下:

typeArrayOop allocate(int length, TRAPS) {
return allocate_common(length, true, THREAD);
} typeArrayOop TypeArrayKlass::allocate_common(int length, bool do_zero, TRAPS) {
assert(log2_element_size() >= 0, "bad scale");
if (length >= 0) {
if (length <= max_length()) {
size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
KlassHandle h_k(THREAD, this);
typeArrayOop t;
CollectedHeap* ch = Universe::heap();
if (do_zero) {
t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL);
} else {
t = (typeArrayOop)CollectedHeap::array_allocate_nozero(h_k, (int)size, length, CHECK_NULL);
}
return t;
}
}
}

参数length表示创建数组的大小,而do_zero表示是否需要在分配数组内存时,将内存初始化为堆值。方法首先调用typeArrayOopDesc::object_size()方法从_layout_helper中获取数组的大小,方法的实现如下:

static int object_size(int lh, int length) {
int instance_header_size = Klass::layout_helper_header_size(lh);
int element_shift = Klass::layout_helper_log2_element_size(lh); julong size_in_bytes = length;
size_in_bytes <<= element_shift;
size_in_bytes += instance_header_size;
// 按8字节对齐,填充的一部分
julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize); return align_object_size((intptr_t)size_in_words); // 对齐,填充的一部分
}

之前介绍过,当为ArrayKlass时,_layout_helper属性是个组合数字,调用 Klass::layout_helper_header_size()方法获取数组头元素的字节数;调用Klass::layout_helper_log2_element_size()方法获取数组元素的大小,对于数组元素是boolean类型来说,这个值为1。

size = instance_header_size + length<<element_shift + 对齐填充

也就是对象头加上实例数据,然后再加上对齐填充。  

在TypeArrayKlass::allocate_common()方法中获取到TypeArrayOopDesc对象所需要分配的内存大小后,就会调用CollectedHeap::array_allocate()或CollectedHeap::array_allocate_nozero()方法在堆上分配内存空间,关于在堆上分配内存的方法在后面介绍垃圾回收时会详细介绍,这里不介绍。

objArrayOop的创建与typeArrayOop的创建非常类似,也是调用oopFactory类中的工厂方法new_objectArray()方法,然后调用ObjArrayKlass::allocate()方法,这里不在介绍。

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

关注公众号,有HotSpot源码剖析系列文章!

 

最新文章

  1. Html5选择本地视频音频文件播放
  2. c# 与java之间的简单区别
  3. atitit.提升开发效率---动态语言总结
  4. 获得输入框的文本document.getElementById(&#39;id&#39;).value;
  5. GO数据库
  6. sharepoint 2010 中获取system账号的真实账号
  7. 22、DDMS(转载)
  8. cocos2d-x游戏开发系列教程-超级玛丽03-main函数
  9. The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement
  10. 【社交系统研发日记】如何在 Laravel 中 “规范” 的开发验证码发送功能
  11. javaweb后台转码
  12. 【原创】大叔经验分享(1)在yarn上查看hive完整执行sql
  13. 2019.3.22 JMeter基础操作
  14. Task Schedule HDU - 3572(按时间点建边)
  15. Webservice和EJB的区别
  16. 内存占用过高 kill 调整mysql内存占用
  17. WebRTC互联网实时通信
  18. 源码分析四(HashMap与HashTable的区别 )
  19. thinkCMF----导航高亮显示
  20. [LeetCode] 199. Binary Tree Right Side View_ Medium tag: BFS, Amazon

热门文章

  1. .NETCore微服务探寻(一) - 网关
  2. Spring Boot 分离资源文件打包
  3. opencv C++ Mat构造函数
  4. 重学 Java 设计模式:实战命令模式「模拟高档餐厅八大菜系,小二点单厨师烹饪场景」
  5. 吃货联盟订餐系统 源代码 Java初级小项目
  6. 《Elasticsearch 权威指南》阅读笔记
  7. android屏幕适配的全攻略--支持不同的屏幕尺寸适配平板和手机
  8. git和github入门指南(2.2)
  9. .Net: C#中的委托(Delegate)和事件(Event)
  10. Python中的@staticmethod和@classmethod的区别