相关文章
Java虚拟机系列算法
在前一篇文章中咱们学习了Java虚拟机的结构原理与运行时数据区域,那么咱们大概知道了Java虚拟机的内存的概况,那么内存中的数据是如何建立和访问的呢?这篇文章会给你答案。数组
对象的建立一般是经过new一个对象而已,当虚拟机接收到一个new指令时,它会作以下的操做。
(1)判断对象对应的类是否加载、连接、初始化
虚拟机接收到一条new指令时,首先会去检查这个指定的参数是否能在常量池中定位到一个类的符号引用,而且检查这个符号引用表明的类是否已被类加载器加载、连接和初始化过。若是没有则先执行相应的类加载过程。关于类加载器咱们在前一篇文章中已经提到过,这里再也不赘述。安全
(2)为对象分配内存
类加载完成后,接着会在Java堆中划分一块内存分配给对象。内存分配根据Java堆是否规整,有两种方式:微信
Java堆的内存是否规整根据所采用的来及收集器是否带有压缩整理功能有关,关于垃圾收集器,本系列后面的文章会介绍。并发
(3)处理并发安全问题
建立对象是一个很是频繁的操做,因此须要解决并发的问题,有两种方式:app
(4)初始化分配到的内存空间
将分配到的内存,除了对象头都初始化为零值。oop
(5)设置对象的对象头
将对象的所属类、对象的HashCode和对象的GC分代年龄等数据存储在对象的对象头中。源码分析
(6)执行init方法进行初始化
执行init方法,初始化对象的成员变量、调用类的构造方法,这样一个对象就被建立了出来。布局
对象建立完毕,而且已经在Java堆中分配了内存,那么对象在堆内存是如何进行布局的呢?
以HotSpot虚拟机为例,对象在堆内存的布局分为三个区域,分别是对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。学习
对象的内存布局以下图所示。
HotSpot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型,OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象实例的具体类型。
HotSpot中,用instanceOopDesc 和 arrayOopDesc 来描述对象头,其中arrayOopDesc对象用于描述数组类型。
instanceOopDesc的代码以下所示。
openjdk/hotspot/src/share/vm/oops/instanceOop.hpp
class instanceOopDesc : public oopDesc {
public:
// aligned header size.
static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
// If compressed, the offset of the fields of the instance may not be aligned.
static int base_offset_in_bytes() {
// offset computation code breaks if UseCompressedClassPointers
// only is true
return (UseCompressedOops && UseCompressedClassPointers) ?
klass_gap_offset_in_bytes() :
sizeof(instanceOopDesc);
}
static bool contains_field_offset(int offset, int nonstatic_field_size) {
int base_in_bytes = base_offset_in_bytes();
return (offset >= base_in_bytes &&
(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
}
};复制代码
能够看出instanceOopDesc继承自oopDesc:
openjdk/hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
// Fast access to barrier set. Must be initialized.
static BarrierSet* _bs;
...
}复制代码
oopDesc中包含两个数据成员:_mark 和 _metadata。其中markOop类型的_mark对象指的是前面讲到的Mark World。_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针,它们就是前面讲到的元数据指针,这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
instanceKlass的代码以下所示。
openjdk/hotspot/src/share/vm/oops/instanceKlass.hpp
class InstanceKlass: public Klass {
...
enum ClassState {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};
...
}复制代码
instanceKlass继承自Klass ,枚举ClassState 用来标识对象的加载进度。
知道了OOP-Klass模型,咱们就能够分析Java虚拟机是如何经过栈帧中的对象引用找到对应的对象实例,以下图所示。
从图中能够看出,经过栈帧中的对象引用找到Java堆中的instanceOopDesc对象,再经过instanceOopDesc中的元数据指针来找到方法区中的instanceKlass,从而肯定该对象的具体类型。
参考资料
《深刻理解Java虚拟机》
《JAVA虚拟机精讲》
深刻探究 JVM | klass-oop 对象模型研究
JVM源码分析之Java对象的建立过程
JVM源码分析之Java类的加载过程
欢迎关注个人微信公众号,第一时间得到博客更新提醒,以及更多成体系的Android相关原创技术干货。
扫一扫下方二维码或者长按识别二维码,便可关注。