JVM--JVM内存布局与Java对象的定义

JVM基本结构

如图所示:
这里写图片描述

运行时数据区

Java虚拟机定义了若干种程序运行时会用到的运行时数据区。

程序计数器

每个Java线程都拥有自己的程序计数器,字节码执行游标。

虚拟机栈

每个Java线程都拥有自己的虚拟机栈,在线程创建时同时创建,用于存储栈帧。栈帧是用来存储数据和部分过程结果的数据结构,用来处理动态链接,方法返回和异常分派。栈帧随着方法的调用而创建,随着方法的返回(正常或抛异常)而销毁,栈帧中含有本地变量表,操作数栈,和指向当前方法的类的运行期常量池引用。栈溢出抛StackOverFlowError。

本地方法栈

功能上类似虚拟机栈,作用于本地方法

方法区

方法区是线程共享的内存区域。类似于传统语言中的编译代码存储区,它主要存储每个类的结构信息和JIT编译后的代码等。方法区溢出抛OOM。

堆区是线程共享的内存区域,类实例以及数组对象都在堆区存储。堆溢出抛OOM。

运行时常量池

运行时常量池主要存放编译器生成的各种字面量和符号引用。

hotspot中的运行时常量池和方法区

hotspot在jdk7以前运行时常量池在方法区分配,jdk7以后运行时常量池移到了堆区,主要影响为String.intern()方法不用像之前在方法区重新生成新的对象,而转为在堆中直接持有对象引用,并避免了方法区内存溢出。

hotspot在jdk8以后将方法区由原PermSpace移至MetaSpace,在本地内存分配,其大小上限取决于系统本地内存大小。此后对于Proxy、CGLIB和JSP等大量动态生成Class的情况下,方法区内存将更为充足。
对于僵死的类及类加载器的垃圾回收将在元数据使用到“MaxMeta-spaceSize”参数的设定值时进行。测试虚拟机启动需要的MetaSpace大小后,可以调整MaxMetaspaceSize参数来减少GC的次数。

对象的内存布局

Hotspot虚拟机使用OOP-Klass二分模型,即将一个java对象分成该对象的实例数据部分和该类的类型信息部分,具体如下:

  • OOP :即普通对象指针,描述对象实例信息。
  • Klass:java类的c++对等体,用来描述该对象的Java类。

对象内存布局主要分为三个部分:对象头、实例数据和对齐填充。其中,对齐填充主要方便内存管理,没有特别意义。如图:
这里写图片描述
注:如果是普通对象,则没有数组长度部分;如果是数组对象就需要加上数组长度。

MarkWord的设计类似于网络协议报文头,将MarkWord划分为多个bit区间用于表示不同的信息。主要用于存储对象自身的运行时数据:hashcode、GC分代年龄、锁状态标志、锁记录指针、锁线程ID等。

对象头中的元数据指针用来指向对象的类信息,虚拟机需要这个指针来定位位于方法区中的对象类型信息。实例数据即类定义中的各个字段内容。

我们通过HSDB实际观察一下Eclipse的主线程对象:_mark(MarkWord),_metadata_compressed_Klass(元数据指针)和下面的实例数据。
这里写图片描述

对象的访问机制

在方法执行过程中,我们通过栈上的变量引用来访问堆中的对象实例,再通过类型指针来访问实例的类型数据,下图体现了hotspot中对象的访问机制。
这里写图片描述

《深入理解Java虚拟机》
《hotspot实战》
《实战Java虚拟机》
https://www.javacodegeeks.com/2013/02/java-8-from-permgen-to-metaspace.html

猜你喜欢

转载自blog.csdn.net/john_lw/article/details/79723949