前段时间,一直在看《Hotspot实战》,顺便编译了一份OpenJDK的源码,而后就在eclipse里面调试起来。c++
虽然个人入门语言是c/c++,可是被Java拉过来好几年了,如今再看源码,熟悉又陌生,好在慢慢找到了感受。数据结构
这是分析Hotspot源码的第一篇,讲一下Klass和Oop这两种数据结构。eclipse
系统的介绍和讲解,能够查看https://yq.aliyun.com/articles/20279和http://www.jianshu.com/p/252e27863822这两篇文章。确实写的比较好,深刻并且透彻。另外能够再看看《Hotspot实战》的第3章。oop
我这里主要看看Klass和Oop的内存布局。布局
当时看这张图的时候,有一个巨大的疑问,instanceKlass的数据结构与图不符,没有对象头(Mark和Klass这两个属性)。this
真是百思不得其解。spa
因而,继续看源码。指针
1)对象头里的Klass这个属性,是一个klassOopDesc类型的指针,并非一个指向Klass类的指针。why???调试
看了以前的第一篇文章,豁然开朗。在JDK8以前,方法区内的描述类型的元数据对象,也是由GC管理的。全部由GC统一管理的对象,都要继承自oopDesc,因此才会诞生klassOopDesc这个类型。从JDK8开始,类型元数据都移出了GC堆,因此Klass这个属性能够直接指向Klass类了。code
2)klassOopDesc只有从父类继承过来的Mark和Klass这两个属性,并无指向Klass类的指针,那oop是如何找到对应的Klass的呢???
klassOopDesc内部有一个方法:klass_part()
// returns the Klass part containing dispatching behavior Klass* klass_part() const { return (Klass*)((address)this + sizeof(klassOopDesc)); }
这个方法果真返回一个指向Klass的指针,可是计算过程比较诡异,在当前klassOopDesc对象的首地址增长sizeof(klassOopDesc)这么多空间后的地址。也就是说,一个klassOopDesc对象数据和对应的Klass对象数据,是从上到下紧密的排列着,有了klassOopDesc的指针,就能顺藤摸瓜找到对应的Klass数据。
那当初构造的时候,是按这种模式在内存分配数据的吗???
以前的第二篇文章,讲的巨细。能够看到,在类加载的过程当中,构造了一个空的Klass对象,而后调用了Klass类的as_klassOop方法返回klassOopDesc的指针。
// returns the enclosing klassOop klassOop as_klassOop() const { // see klassOop.hpp for layout. return (klassOop) (((char*) this) - sizeof(klassOopDesc)); }
klassOop是klassOopDesc指针的一个别名。构造的时候,首地址减去sizeof(klassOopDesc)这么多空间后的地址。这时候,内存空间是分配出来了,具体的数据,在后续过程当中填充。
最后说下看源码心得体会 :其实那张图上的数据排列结构是对的,可是具体的实现方式,若是不看源码确定会很疑惑。另外,看到这么犀利的指针操做,对c/c++的好感真是倍增。