HotSpot的对象模型(6)
接着上一篇,我们继续来讲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的源代码
关注公众号,有HotSpot源码剖析系列文章!
HotSpot的对象模型(6)的更多相关文章
- HotSpot的对象模型(5)
Java对象通过Oop来表示.Oop指的是 Ordinary Object Pointer(普通对象指针).在 Java 创建对象实例的时候创建,用于表示对象的实例信息.也就是说,在 Java 应用程 ...
- HotSpot的执行引擎-CallStub栈帧
之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...
- Java虚拟机(二)对象的创建与OOP-Klass模型
前言 在前一篇文章中我们学习了Java虚拟机的结构原理与运行时数据区域,那么我们大概知道了Java虚拟机的内存的概况,那么内存中的数据是如何创建和访问的呢?这篇文章会给你答案. 1.对象的创建 对象的 ...
- 操作句柄Handle(7)
可以将Handle理解成访问对象的一个“句柄”.垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节. Handle涉及到的相关类的继承关系如下图所示. ...
- 句柄Handle的释放(8)
本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放. 1.HandleArea.Area与Chunk 句柄都是在HandleArea中分配并存储的,类的定义如下: // Thread ...
- Java主类的装载
在JavaMain()函数中调用LoadMainClass()函数加载Java主类.LoadMainClass()函数的实现如下: /* * Loads a class and verifies th ...
- 解析Class文件
类文件解析的入口是ClassFileParser类中定义的parseClassFile()方法.上一小节得到了文件字节流stream后,接着会在ClassLoader::load_classfile( ...
- klassVtable与klassItable
klassVtable与klassItable类用来实现Java方法的多态,也可以叫动态绑定,是指在应用执行期间通过判断接受对象的实际类型,根据实际类型调用对应的方法.C++为了实现多态,在对象中嵌入 ...
- 字段解析之OopMapBlock(4)
OopMapBlock是一个简单的内嵌在Klass里面的数据结构,用来描述oop中包含的引用类型属性,即该oop所引用的其他oop在oop中的内存分布,然后就可以根据当前oop的地址找到所有引用的其他 ...
随机推荐
- Tensorflow中Tensor对象的常用方法(持续更新)
Tensor是Tensorflow中重要的对象.下面是Tensor的常用方法,后面还会写一篇随笔记录Variable的用法. 1. 生成一个(常)Tensor对象 >>>A = tf ...
- 使用Bundle在Activity中交换数据
大概过程 编写demo activity_main.xml <?xml version="1.0" encoding="utf-8"?> <R ...
- 套接字TCP控制台服务器程序代码示范
套接字TCP控制台服务器程序代码示范 https://blog.csdn.net/txwtech/article/details/90344081
- upd套接字服务器与客户端交互C++代码示范
upd套接字服务器与客户端交互C++代码示范 // vc2_2_4UDPserver_Txwtech.cpp : 定义控制台应用程序的入口点. //服务器端 #include "StdAfx ...
- package.json 文件说明:
package.json 文件属性说明: name - 包名. version - 包的版本号. description - 包的描述. homepage - 包的官网 url . author - ...
- 尚硅谷ajax视频教程1
1.+尚硅谷_佟刚_Ajax_概述.wmv 2.+尚硅谷_佟刚_Ajax_使用+XMLHttpRequest+实现+Ajax.wmv XMLHttpRequest 对象提供了对 HTTP 协议的完全的 ...
- springmvc-实现增删改查
30. 尚硅谷_佟刚_SpringMVC_RESTRUL_CRUD_显示所有员工信息.avi现在需要使用restful风格实现增删改查,需要将post风格的请求转换成PUT 请求和DELETE 请求 ...
- vue element安装
element-ui插件 安装 >: cnpm i element-ui -S main.js配置 import ElementUI from 'element-ui'; import 'ele ...
- Ueditor富文本添加视频内容,视频不显示以及编辑富文本时,视频不显示解决方案
问题是在添加视频时,编辑器会把视频标签<video>换成<img>.很讨厌... 1.2是解决添加视频时不显示,3是解决编辑时不显示 ueditor.all.js文件中 第7 ...
- java命令行输入参数
Java命令行输入参数 代码用例:命令行输入参数,并进行加法运算. public class Demo01 { public static void main(String[] args) { for ...