jvm理论-运行时数据区

三大流行jvm

sun HotSpot

ibm j9

BEA JRockit

Oracle 会基于HotSpot整合 JRockit。

jvm运行时数据区

java虚拟机所管理的内存将会包括以下几个运行时数据区域

jvm理论-运行时数据区

程序计数器

1、线程私有,随线程而生,随线程而灭。

2、如果线程在执行java方法,计数器记录正在执行的虚拟机字节码指令的地址。

3、如果线程在执行native方法,计数器值为空。

java虚拟机栈

1、线程私有,随线程而生,随线程而灭。

2、java方法执行的内存模型:局部变量表、操作数栈、动态链接、方法出口等。

栈帧

栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)[1]的栈元素。

栈帧存储了方法的局部变量表、操作数栈、栈帧信息(动态连接、方法返回地址、附加信息)。

每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。

一个线程中的方法调用链可能会很长,很多方法都同时处于执行状态。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧(Current StackFrame),与这个栈帧相关联的方法称为当前方法(Current Method)。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。

jvm理论-运行时数据区

局部变量表

局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

1、容量

局部变量表的容量以变量槽(variable Slot)为最小单位。

boolean,byte,char,short,int,float,reference,returnAddress占一个slot。

long,double占两个slot。

reference:表示对一个对象实例的引用。

1)、从此引用可直接或间接找到对象在java堆中的数据存放的起始地址索引。

2)、从此索引直接或间接找到对象所属类型在方法区中的存储的类型信息。

returnAddress:为字节码指令jsr、jsr_w、ret服务的,指向一条字节码指令地址。

2、容量大小和索引定位

编译时已经确定。

虚拟机通过索引定位的方式使用局部变量表。索引从0开始,最大值为slot的数量。

在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程的,如果执行的是实例方法(非static的方法),那局部变量表中第0位索引的Slot默认是用于传递方法所属对象实例的引用,在方法中可以通过关键字”this”来访问到这个隐含的参数。其余参数则按照参数表顺序排列,占用从1开始的局部变量Slot,参数表分配完毕后,再根据方法体内部定义的变量顺序和作用域分配其余的Slot。

3、局部变量与类变量

类变量有两次赋初始值的过程,一次在准备阶段,赋予系统初始值;另外一次在初始化阶段,赋予程序员定义的初始值。因此,即使在初始化阶段程序员没有为类变量赋值也没有关系,类变量仍然具有一个确定的初始值

但局部变量就不一样,如果一个局部变量定义了但没有赋初始值是不能使用的。

操作数栈

操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出(Last In First Out,LIFO)栈。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long和double。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。在方法执行的任何时候,操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。

当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。

举个例子,整数加法的字节码指令iadd在运行的时候操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令时,会将这两个int值出栈并相加,然后将相加的结果入栈。

Java虚拟机的解释执行引擎称为”基于栈的执行引擎”,其中所指的”栈”就是操作数栈。

动态链接

每个栈帧都包含一个指向运行时常量池[1]中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。

Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接
引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。

方法返回地址

当一个方法开始执行后,只有两种方式可以退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者(调用当
前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。
另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用athrow字节码指令产生的异
常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常
完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。

附加信息

虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧之中,例如与调试相关的信息,这部分信息完全取决于具体的虚拟机实现,这里不再详述。在实际开发
中,一般会把动态连接、方法返回地址与其他附加信息全部归为一类,称为栈帧信息。

本地方法栈

1、线程私有,随线程而生,随线程而灭。

2、为虚拟机使用到的native方法服务。

java堆

1、线程共享。

2、虚拟机启动时创建。

3、存放对象实例:所有的对象实例以及数组都要在堆上分配。

4、分为:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。

年轻代又分为:Eden空间、From Survivor空间、To Survivor空间。

jvm理论-运行时数据区

java heap是GC管理的主要区域。

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型: Scavenge GCFull GC

Scavenge GC

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC

对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:

· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化

方法区

1、线程共享。

2、存储已被虚拟机加载的类信息、常量、静态代码、jit后的代码等。

3、HotSpot中,方法区也被称为”永久代”,被GC管理。

运行时常量池:存放编译期生成的各种字面量和符号引用。可以是编辑期常量也可以是运行期生成的常量,如String的intern()方法。

直接内测

NIO引入的通道(channel)和缓存区(buffer)的I/O方式,可以使用Native函数在堆外分配内存,在java堆中通过DirectByteBuffer对象作为这块内存的引用进行操作。

JVM对象

对象创建(new)

1、检测指令的参数能否在常量池中定位到一个类的符号引用。

2、检测这个符号引用代表的类是否被加载、链接、初始化,如果没有先进行类加载。

3、为新生对象分配内存。

根据java heap是否规整,通过指针碰撞(Bump the pointer)或空闲列表(Free list)分配内存。

内存是否规整,取决于GC是否有压缩整理功能。

4、对对象进行必要设置,这些设置信息存放在对象头中。

执行完new以后执行

对象的内存结构

对象头 Header

1、包含两部分数据:

Mark Word:存储对象自身的运行时数据,如hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64的虚拟机(未开启指针压缩)中分别为4B和8B,官方称之为”Mark Word”。

类型指针 KClass:即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。

数组长度:如果对象是一个Java数组,那再对象头中还必须有一块用于记录数组长度的数据。

对象头在32位系统上占用8B,64位系统上占16B。 无论是32位系统还是64位系统,对象都采用8字节对齐。Java在64位模式下开启指针压缩,比32位模式下,头部会大4B(mark区域变位8B,kclass区域被压缩),如果没有开启指针压缩,头部会大8B(mark和kclass都是8B),换句话说,
HotSpot的对齐方式为8字节对齐:(对象头+实例数据+padding)%8 等于0 且 0

Original: https://www.cnblogs.com/tenghoo/p/jvm_runtime.html
Author: 青羽
Title: jvm理论-运行时数据区

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/533699/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球