引言
java内存模型,java内存结构(JVM内存结构)和java对象模型是我们要知道的概念,
JVM内存结构,和Java虚拟机的运行时区域有关。 Java内存模型,和Java的并发编程有关。 Java对象模型,和Java对象在虚拟机中的表现形式有关。
java程序要运行,会涉及到java线程与操作系统线程的交互,这时就需要java虚拟机做中间商,因此我们可以简单了解在JVM中的java对象模型(参考这篇,其实也是转载,个人觉得这是个狼人)
java对象模型
本文的所有分析均基于HotSpot虚拟机(版本有点低,应该是jdk7,但从网上找到一篇又新又好的挺难的,还是简单看看吧)
1.HotSpot
是基于c++实现,而c++是一门面向对象的语言,本身是具备面向对象基本特征的,所以Java中的对象表示,最简单的做法是为每个Java类生成一个c++类与之对应。
但HotSpot JVM并没有这么做,而是设计了一个OOP-Klass Model
。OOP(Ordinary Object Pointer
)指的是普通对象指针,而Klass
用来描述对象实例的具体类型。HotSopt JVM的设计者把对象一拆为二,分为klass
和oop
,其中oop
的职能主要在于表示对象的实例数据,所以其中不含有任何虚函数。而klass为了实现虚函数多态,所以提供了虚函数表。
为什么HotSpot要设计一套oop-klass model
呢?
答案是:HotSopt JVM的设计者不想让每个对象中都含有一个vtable
(虚函数表),与多态有关
2.多态
- 是面向对象的最主要的特性之一,是一种方法的动态绑定,实现运行时的类型决定对象的行为。
- 多态的表现形式是父类指针或引用指向子类对象,在这个指针上调用的方法使用子类的实现版本。
- 多态是IOC、模板模式实现的关键。
在C++中
通过虚函数表的方式实现多态,每个包含虚函数的类都具有一个虚函数表(virtual table),在这个类对象的地址空间的最靠前的位置存有指向虚函数表的指针。在虚函数表中,按照声明顺序依次排列所有的虚函数。由于C++在运行时并不维护类型信息,所以在编译时直接在子类的虚函数表中将被子类重写的方法替换掉。
在Java中
在运行时会维持类型信息以及类的继承体系。每一个类会在方法区中对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。其中,类型信息具有superclass属性指示了其超类,以及这个类对应的方法表(其中只包含这个类定义的方法,不包括从超类继承来的)。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。
这就是java对象模型,如下例子
class Model
{
public static int a = 1;
public int b;
public Model(int b) {
this.b = b;
}
}
public static void main(String[] args) {
int c = 10;
Model modelA = new Model(2);
Model modelB = new Model(3);
}
这就是代码在JVM上的 存储结构关系。 JVM在后面会介绍
下面这幅图读者可以忽略,是我留存的,不知道对不对
下面再简单了解一下jvm中java对象样子
我们需要知道一下opp-klass模型,opp-klass模型的整体定义,在HotSpot的源码中可以找到。oops模块可以分成两个相对独立的部分:OOP框架和Klass框架。我的理解就是jvm把java对象和类分开表示,容易gc
简单看看就行,oop体系
//定义了oops共同基类
typedef class oopDesc* oop;
//表示一个Java类型实例
typedef class instanceOopDesc* instanceOop;
//表示一个Java方法
typedef class methodOopDesc* methodOop;
//表示一个Java方法中的不变信息
typedef class constMethodOopDesc* constMethodOop;
//记录性能信息的数据结构
typedef class methodDataOopDesc* methodDataOop;
//定义了数组OOPS的抽象基类
typedef class arrayOopDesc* arrayOop;
//表示持有一个OOPS数组
typedef class objArrayOopDesc* objArrayOop;
//表示容纳基本类型的数组
typedef class typeArrayOopDesc* typeArrayOop;
//表示在Class文件中描述的常量池
typedef class constantPoolOopDesc* constantPoolOop;
//常量池告诉缓存
typedef class constantPoolCacheOopDesc* constantPoolCacheOop;
//描述一个与Java类对等的C++类
typedef class klassOopDesc* klassOop;
//表示对象头
typedef class markOopDesc* markOop;
当我们使用new
创建一个Java对象实例的时候,JVM会创建一个instanceOopDesc
对象来表示这个Java对象。同理,当我们使用new
创建一个Java数组实例的时候,JVM会创建一个arrayOopDesc
对象来表示这个数组对象。
下面看看,oopDesc类定义,也是java对象在jvm中的表示
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;
narrowOop _compressed_klass;
} _metadata;
private:
// field addresses in oop
void* field_base(int offset) const;
jbyte* byte_field_addr(int offset) const;
jchar* char_field_addr(int offset) const;
jboolean* bool_field_addr(int offset) const;
jint* int_field_addr(int offset) const;
jshort* short_field_addr(int offset) const;
jlong* long_field_addr(int offset) const;
jfloat* float_field_addr(int offset) const;
jdouble* double_field_addr(int offset) const;
address* address_field_addr(int offset) const;
}
class instanceOopDesc : public oopDesc {
}
class arrayOopDesc : public oopDesc {
}
HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头、实例数据和对齐填充。在虚拟机内部,一个Java对象对应一个instanceOopDesc
的对象。其中对象头包含了两部分内容:_mark
和_metadata
,而实例数据则保存在oopDesc中定义的各种field中。这是对java对象的描述,接下来是对类的描述。
klass体系
//klassOop的一部分,用来描述语言层的类型
class Klass;
//在虚拟机层面描述一个Java类
class instanceKlass;
//专有instantKlass,表示java.lang.Class的Klass
class instanceMirrorKlass;
//专有instantKlass,表示java.lang.ref.Reference的子类的Klass
class instanceRefKlass;
//表示methodOop的Klass
class methodKlass;
//表示constMethodOop的Klass
class constMethodKlass;
//表示methodDataOop的Klass
class methodDataKlass;
//最为klass链的端点,klassKlass的Klass就是它自身
class klassKlass;
//表示instanceKlass的Klass
class instanceKlassKlass;
//表示arrayKlass的Klass
class arrayKlassKlass;
//表示objArrayKlass的Klass
class objArrayKlassKlass;
//表示typeArrayKlass的Klass
class typeArrayKlassKlass;
//表示array类型的抽象基类
class arrayKlass;
//表示objArrayOop的Klass
class objArrayKlass;
//表示typeArrayOop的Klass
class typeArrayKlass;
//表示constantPoolOop的Klass
class constantPoolKlass;
//表示constantPoolCacheOop的Klass
class constantPoolCacheKlass;
Klass向JVM提供两个功能:
- 实现语言层面的Java类(在Klass基类中已经实现)
- 实现Java对象的分发功能(由Klass的子类提供虚函数实现)
instanceKlass,也是java类在jvm中的表现
//类拥有的方法列表
objArrayOop _methods;
//描述方法顺序
typeArrayOop _method_ordering;
//实现的接口
objArrayOop _local_interfaces;
//继承的接口
objArrayOop _transitive_interfaces;
//域
typeArrayOop _fields;
//常量
constantPoolOop _constants;
//类加载器
oop _class_loader;
//protected域
oop _protection_domain;
....
在JVM中,对象在内存中的基本存在形式就是oop。那么,对象所属的类,在JVM中也是一种对象,因此它们实际上也会被组织成一种oop,即klassOop。同样的,对于klassOop,也有对应的一个klass来描述,它就是klassKlass,也是klass的一个子类。klassKlass作为oop的klass链的端点。
注:klassKlass好像jdk8的时候已经去掉了
关于对象和数组的klass链大致如下图:
在这种设计下,JVM对内存的分配和回收,都可以采用统一的方式来管理。oop-klass-klassKlass关系如图:
java内存结构(JVM)
这个网上太多,就不详细讲了,上图
jdk7中的持久代已经移除,本来是在堆内存中,jdk8中移到本地内存,变成元数据区
方法区可以理解为元数据区,有些微差别可以百度
里面东西不懂的可以百度
在关于JVM的内存结构的图中,我们可以看到,其中Java堆和方法区的区域是多个线程共享的数据区域。也就是说,多个线程可能可以操作保存在堆或者方法区中的同一个数据。这也就是我们常说的“Java的线程间通过共享内存进行通信”。会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型
JMM
这个也不多说了,我之前在并发的文章已经说了https://blog.csdn.net/a_higher/article/details/109809229