接着上一篇,我们继续来讲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源码剖析系列文章!

 

HotSpot的对象模型(6)的更多相关文章

  1. HotSpot的对象模型(5)

    Java对象通过Oop来表示.Oop指的是 Ordinary Object Pointer(普通对象指针).在 Java 创建对象实例的时候创建,用于表示对象的实例信息.也就是说,在 Java 应用程 ...

  2. HotSpot的执行引擎-CallStub栈帧

    之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...

  3. Java虚拟机(二)对象的创建与OOP-Klass模型

    前言 在前一篇文章中我们学习了Java虚拟机的结构原理与运行时数据区域,那么我们大概知道了Java虚拟机的内存的概况,那么内存中的数据是如何创建和访问的呢?这篇文章会给你答案. 1.对象的创建 对象的 ...

  4. 操作句柄Handle(7)

    可以将Handle理解成访问对象的一个“句柄”.垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节. Handle涉及到的相关类的继承关系如下图所示. ...

  5. 句柄Handle的释放(8)

    本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放. 1.HandleArea.Area与Chunk 句柄都是在HandleArea中分配并存储的,类的定义如下: // Thread ...

  6. Java主类的装载

    在JavaMain()函数中调用LoadMainClass()函数加载Java主类.LoadMainClass()函数的实现如下: /* * Loads a class and verifies th ...

  7. 解析Class文件

    类文件解析的入口是ClassFileParser类中定义的parseClassFile()方法.上一小节得到了文件字节流stream后,接着会在ClassLoader::load_classfile( ...

  8. klassVtable与klassItable

    klassVtable与klassItable类用来实现Java方法的多态,也可以叫动态绑定,是指在应用执行期间通过判断接受对象的实际类型,根据实际类型调用对应的方法.C++为了实现多态,在对象中嵌入 ...

  9. 字段解析之OopMapBlock(4)

    OopMapBlock是一个简单的内嵌在Klass里面的数据结构,用来描述oop中包含的引用类型属性,即该oop所引用的其他oop在oop中的内存分布,然后就可以根据当前oop的地址找到所有引用的其他 ...

随机推荐

  1. IOS 发布 程序截图问题

    特别要注意那个有无状态栏时的像素要求 **注意:在截屏模拟器的时候,请把模拟器的Scale设置成100%(Window->Scale->100%) 开模拟器截图,运行每一个iOS型号,然后 ...

  2. rust 生命周期2

    之前定义的结构体,都是不含引用的. 如果想定义含引用的结构体,请定义生命周期注解 #[warn(unused_variables)] struct ImportantExcerpt<'a> ...

  3. springboot 配置本地文件映射路径

    @Configuration public class MyBlogWebMvcConfigurer extends WebMvcConfigurerAdapter { @Autowired priv ...

  4. 性能测试之JVM的故障分析工具VisualVM

    VisualVM 是随JDK一同发布的jvm诊断工具,通过插件可以扩展很多功能,插件扩展也是其精华所在. 提供了一个可视界面,用于在Java应用程序在Java虚拟机上运行时查看有关Java应用程序的详 ...

  5. 用 npm 搭建vue项目

    一.开发环境 vue推荐开发环境: Node.js: javascript运行环境(runtime),不同系统直接运行各种编程语言 npm: Nodejs下的包管理器. webpack: 它主要的用途 ...

  6. 黎活明8天快速掌握android视频教程--12_文件的保存与读取

    1.当前是把文件保存当前手机的app的data目录下 我们来看看操作保存文件的业务类 package contract.test.savafileapplication; import android ...

  7. Java面试必备Springioc上

    配置文件中 Proprety name值必须和 类中的成员变量private IUsedao  userDao一一对应 工程项目的代码为:

  8. 【String注解驱动开发】你了解@PostConstruct注解和@PreDestroy注解吗?

    写在前面 在之前的文章中,我们介绍了如何使用@Bean注解指定初始化和销毁的方法,小伙伴们可以参见<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!& ...

  9. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  10. 云计算课程实验之安装Hadoop及配置伪分布式模式的Hadoop

    一.实验目的 1. 掌握Linux虚拟机的安装方法. 2. 掌握Hadoop的伪分布式安装方法. 二.实验内容 (一)Linux基本操作命令 Linux常用基本命令包括: ls,cd,mkdir,rm ...