JVM学习(2) -- 运行时数据区

目录

运行时数据区(Run-Time Data Areas)

官网概括

图解 

常规理解

方法区(Method Area)

堆(Heap)

虚拟机栈(Java Virtual Machine Stacks)

程序计数器(PC Register)

本地方法栈(Native Method Stacks)

结合字节码指令理解虚拟机栈和栈帧

指向关系

栈指向堆

方法区指向堆

堆指向方法区

Java对象内存布局


运行时数据区(Run-Time Data Areas)

装载的第(2)(3)步有听到运行时数据,堆,方法去等名词

(2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3) Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口
 
说白了就是类文件被类装载器装载进来之后,类中的内容 ( 比如变量,常量,方法,对象等这些数 据得要有个去处,也就是要存储起来,存储的位置肯定是在 JVM 中有对应的空间 )
 
 

官网概括

The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.

图解 

常规理解

方法区(Method Area)

  1. 方法区是各个线程共享的内存区域,在虚拟机启动时创建。
  2. 用于存储已被虚拟机加载的类息、常量、静态变量、即时编译器编译后的代码等数据。
  3. 虽然Java虚拟机规范把方法去描述为堆的一个逻辑部分,但是它却又一个别名No-Heap,自然是与Java堆分开来的。
  4. 当方法区无法满足分配需求时,将抛出OutOfMemoryError异常。

此时我们回看装载阶段的第二步;(2)将这个字节流锁代表的静态存储结构转化为方法去的运行时数据结构

如果这时候把从 Class 文件到装载的第 (1) (2) 步合并起来理解的话,可 以画个图

 需要说明的:

  • 方法区在JDK8中是Metaspace,在JDK6、7中是perm space
  • Run-Time constant Pool(运行时常量池):Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译时期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。
  • Eachrun-time constant pool is allocated from the Java Virtual Machine's method area.

堆(Heap)

(1)堆是Java虚拟机锁管理的内存中最大的一块,在虚拟机启动时创建,被所有线程共享

(2)Java对象实例以及数组都在堆上分配

此时回看装载阶段的第 3 步: (3) Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方 法区中这些数据的访问入口
此时装载 (1)(2)(3) 的图可以改动一下

虚拟机栈(Java Virtual Machine Stacks)

经过上面的分析,类加载机制的装载过程已经完成,后续的链接,初始化也会相应的生效。 假如目前的阶段是初始化完成了,后续做啥呢?肯定是 Use 咯,不用的话这样折腾来折腾去 有什么意义?那怎样才能被使用到?换句话说里面内容怎样才能被执行?比如通过主函数 main 用其他方法,这种方式实际上是 main 线程执行之后调用的方法,即要想使用里面的各种内容,得 要以线程为单位,执行相应的方法才行。 那一个线程执行的状态如何维护?一个线程可以执行多少个方法?这样的关系怎么维护呢?
 
(1)虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态,换句话说,一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建。
(2)每一个被线程执行的方法,为该栈中的栈帧,即每一个方法对应一个栈帧。
(3)调用一个方法,就会 向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。
 
写段伪代码 / 画图理解栈和栈帧

 

 

程序计数器(PC Register)

我们都知道一个 JVM 进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据 CPU 调度来的。
假如线程 A 正在执行到某个地方,突然失去了 CPU 的执行权,切换到线程 B 了,然后当线程 A 再获 CPU 执行权的时候,怎么能继续执行呢?这就是需要在线程中维护一个变量,记录线程执行到 的位置。

(1)程序技术其占用的内存空间很小,由于Java虚拟机的多线程是通过线程轮流切换,并分配处理器执行时间的方式来实现的,在任意时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的位置,每条线程需要在一个独立的程序计数器(线程私有)。

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

(3)如果正在执行的是native方法,则计数器为空。

本地方法栈(Native Method Stacks)

如果当前线程执行的方法时Native类型的,这些方法就会在本地方法栈中执行。

 

结合字节码指令理解虚拟机栈和栈帧

栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间。
 
每个栈帧中包括局部变量表 (Local Variables) 、操作数栈 (Operand Stack) 、指向运行时常量池的引用 (A reference to the run-time constant pool) 、方法返回地址 (Return Address) 和附加信息。
 
  • 局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关指令将其加载至操作数栈中作为操作数使用)。
  • 操作数栈:以压栈和出栈的方式存储操作数的。
  • 动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(DynamicLinking)
  • 方法返回地址 : 当一个方法开始执行后 , 只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇 见异常,并且这个异常没有在方法体内得到处理。
  • 附加信息:用来对齐地址。

指向关系

栈指向堆

如果在栈帧中有一个变量,类型为引用类型,比如 Object obj=new Object() ,这时候就是典型的栈中元 素指向堆中的对象。

方法区指向堆

方法区中会存放静态变量,常量等数据。如果是下面这种情况,就是典型的方法区中元素指向堆中的对 象。

堆指向方法区

注意,方法区中会包含类的信息,堆中会有对象,那怎么知道对象是哪个类创建的呢?

Java对象内存布局

一个Java对象在内存中包括3个部分:对象头、实例数据和对齐填充

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值