JVM系列(二)运行时数据区

jvm运行时数据区非常重要也是非常基础的部分,但是这部分又是对于jvm参数优化的前提,所以更需要我们要着重掌握。本篇文章从周志明《深入理解java虚拟机》摘录了一些内容。


运行时数据区

在这里插入图片描述
在这里插入图片描述
JVM执行java程序的时候会把其所管理的内存划分为若干不同的数据区域(根据虚拟机厂商不同会有差异,这里主要是对hotSpot进行讲解),虚拟机规范并不是一成不变的,Oracle在发布新的JAVA版本时,可能会对JVM做一定的优化和改进,例如在JDK8的版本中,方法区被移除,取而代之的是metaspace(元数据空间)。

虚拟机栈

虚拟机栈是线程私有的,有一些列帧组成因此也叫作帧栈,jvm通过
-Xss 设置栈的大小。若单个线程请求的栈深度大于虚拟机允许的深度,则会抛出StackOverflowError(栈溢出错误);不同于StackOverflowError,OutOfMemoryError指的是当整个虚拟机栈内存耗尽,并且无法再申请到新的内存时抛出的异常
栈中存储:局部变量表、操作数栈、动态链接、方法出口
局部变量表中存放了编译期可知的各种:

基本数据类型:boolen byte cahr short int flot long double 。

和程序开发密切相关,堆是线程共享的,也是我们进行jvm优化的主要考虑区域。堆中主要存放 对象的实例信息,在堆的对象头中会存储类信息存储在方法区中的指针,这也是为什么上图堆和方法区会有箭头指向关系的原因。

java堆从内存空间划分会有如下划分:

年轻代: 也有叫做新生代。新生成的对象一般存放在新生代,新生代对象朝生夕死,存活率很低,在新生代中,常规应用进行一次垃圾收集一可以回收70%-95%的空间,回收率很高;在新生代发生的对象回收叫做Young GC /Minor GC,默认占堆内存的 1/3。
年轻代又会被分为 Eden、From、To,默认分配的内存比例为 8: 1 : 1 ,From和To被通称为Survivor区。
老年代: 对象在新生代中经历了很多次GC后(默认15次,对象的年龄也是存储在对象头信息中)仍然存活下来的对象会进入老年代中,老年代中对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢,因此我们进行jvm优化主要是为了减少老年代GC次数。默认占堆内存的 2/3。

方法区

该区域和堆一样也是线程共享的,方法区中主要存储:常量、静态变量、已被虚拟机加载的类信息(基本变量、普通方法)、常量池、即时编译器编译后的代码。另外,方法区包含了一个特殊的区域“运行时常量池”,它们的关系如下图所示:。
在这里插入图片描述
1 常量池

1.1 Class文件中的常量池

在Class文件结构中,最头的4个字节用于存储Megic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。

常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

类和接口的全限定名
字段名称和描述符
方法名称和描述符

1.2 运行时常量池

Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的常量池。

字符串常量池的设计思想

1.字符串的分配和其他对象分配一样,作为最基础的数据类型,大量频繁的创建字符串,极大程度的影响程序的性能
2.JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
· 为字符串开辟了一个字符串常量池,类似于缓冲区
· 创建字符串常量时,首先检查字符串常量池是否存在该字符串
· 存在该字符串,返回引用实例;不存在,实例化该字符串并放入常量池中
在这里插入图片描述

本地方法栈

本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点以及都能抛出StackOverflowError和OutOfMemoryError异常。不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法。

程序计数器

程序计数器是线程私有的,唯一一个在jvm规范中没有规定任何OutofMemoryError的区域,程序计数器占用内存很小,在进行JVM内存计算时,可以忽略不计。程序计数器存储当前线程所执行的字节码指令地址,指向下一条指令的地址, 执行本地方法时,程序计数器的值为undefined。

发布了12 篇原创文章 · 获赞 0 · 访问量 348

猜你喜欢

转载自blog.csdn.net/fd135/article/details/104250721