【JVM基础】运行时数据区


一:初步认识

1.1 几个问题

1. 上一节我们学习了类加载,但我们并没说明类加载到内存后放置在JVM的什么位置?
2. 对象如何存储?无用的对象如何回收?类信息放在哪里?等问题都会在本章解决。

1.2 运行时数据区的图解

在这里插入图片描述

1.3 从多线程的角度看运行时数据区的分类

一个Java程序允许多个线程同时进行,也就是说JVM支持多线程
在这里插入图片描述


二: 程序计数器

2.1 作用

记录当前将要执行的下一条指令的地址。口语化就是:记录当前程序要执行的下一行代码的地址。

2.2 为什么需要程序计数器?

这个问题牵扯到一系列关于程序计数器的面试题。
解决这个问题的首先我们要知道程序的运行本质上就是一个又一个方法的执行,俗话说:栈管运行,堆管内存的分配。当一个方法执行时,会在虚拟机栈上开辟栈帧,程序结束时该方法对应的栈帧就会销毁。

💡:由上个图我们知道,虚拟机栈是线程私有的,那么就会存在这样的问题,现在线程A抢占到CPU资源执行到15行代码的位置,然后线程B抢占到CPU资源执行到第20行代码的位置。如果这时线程A再次抢占到CPU资源,如果线程A不记录上一次结束时下一条语句执行的地址,这就会导致线程A不知道从哪里开始执行,难道还要再重新执行一次?
这显然是不现实的,而又每一个线程执行的情况均是不同的,因此我们又必要为每一个线程配备一个私有的PC程序计数器。


三:虚拟机栈

3.1 初步认知

1. 每个线程在创建时都会创建一个虚拟机栈,其中保存着一个个的栈帧,每一个栈帧对应着一次方法的调用。
2. 生命周期:虚拟机栈的生命周期和它所属的线程的生命周期一致。
3. 作用:主管JAVA程序的运行,栈帧中保存着栈帧所对应方法的局部变量,部分运算结果,方法返回值,并参与方法的调用与返回。

3.2 储存单位-栈帧

在这里插入图片描述

3.3 栈帧的内部结构

3.3.1 概念

栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间。
每个栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向运行时常量池的引用(A reference to the run-time constant pool)、方法返回地址(Return Address)和附加信息。


3.3.2 栈内部结构图

在这里插入图片描述


3.3.3 局部变量表

特征

  • 本质上是一个数组
  • 数组中的每一个单位称为槽
  • 作用:储存方法内部的局部变量,方法的形式参数,方法的返回值
  • 线程安全:每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题。
  • 局部变量表的大小在编译时期就已经确定。
  • 局部变量表是储存在栈上的,栈上不存在GC,所以局部变量表不存在GC。
  • 当局部变量出了其作用域,其对应的槽就会被释放。

3.3.4 操作数栈

特征

  • 本质上是一个栈
  • 以压栈和出栈的方式存储操作数的
  • 存储内容:操作数,运算过程的中间变量,方法的返回值,充当运算过程中的临时存储空间。
  • 作用示例:交换,复制,运算。
  • 栈的深度在编译时期确定

3.3.5 动态链接

概念:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用 过程中的动态连接(Dynamic Linking)。
从字节码文件的角度来分析:一个Java文件可能依赖其它的很多类文件,如果我们把它所依赖的类全部写在这个类的Class文件中,这无疑会使Class文件变得十分臃肿,因此在这个类的Class文件的常量池中会存储所依赖外部类的符号引用。
每一个栈帧都应该存储运行时常量池中该栈帧所对应方法的引用,就是为了在运行过程中动态的将这些符号引用转换为直接引用,从而正常的使用外部类文件。


3.3.6 方法返回地址

概念:方法执行结束时,此时PC寄存器所代表的值就是方法返回地址。
作用:让方法在执行结束时,能够跳转到调用这个方法的位置继续执行代码。


3.4 栈区中的异常

Java 虚拟机规范定义: 栈空间的大小可以是动态的,也可以是固定不变的。

当设置栈的大小为固定大小

如果采用固定大小的方式创建虚拟机栈,那么每一个线程中的栈容量都可以在创建的时候独立选定,但一旦选定就不可以更改,如果栈中剩余的空间不足以分配新栈帧时,就会报StackOverflowError。

当设置栈的大小为动态

如果采用动态扩容的方式创建虚拟机栈,那么当栈的大小不够用的时候,会尝试动态的扩容,如果扩容后还不能满足需求就会报OOM,内存溢出异常。


四 堆区

通俗来说栈管运行,堆管内存。堆区主要的功能是为对象分配内存,
并负责对对象的内存进行回收。

对于堆区的内容我们会在下篇-对象的创建与对象内存分配 详细讲解。


五 方法区

5.1 栈区,堆区与方法区的交互关系

图例:
在这里插入图片描述


5.2 方法区的实现

5.2.1 非堆

在这里插入图片描述

5.2.2 分类

《Java虚拟机规范》中并没有明确的指定堆区的实现方式,JDK1.8以后HotSpotJVM用元空间替代永久代来实现了方法区,方法区就是元空间的说法仅限于HotSpotJVM。

HotSpotJVM实现方法区的方式JDK1.8之前使用的是永久代,JDK1.8之后使用元空间代替永久代实现方法区。

5.2.3 图解

在这里插入图片描述


5.3 方法区内存设置

方法区的大小不必是固定的JVM可以根据需求来动态的调整。

5.3.1 JDK 1.7及以前

  • 通过:-XX:PermSize 来设置永久代的初始空间分配大小。默认值是20.75w。
  • 通过: -xx:MaxPermSize来设置永久代的最大空间分配大小。32位机器默认值是64M,64位机器默认是82M
  • 当JVM类加载的类的总大小超过了永久代的最大容量就会报**OutOfMemoryError:PermGen space**。

5.3.2 JDK 1.8及以后

在这里插入图片描述


5.4 方法区的内部结构

我们可以先看一下《深入理解Java虚拟机》中的描述
在这里插入图片描述

5.4.1 类型信息

在这里插入图片描述

5.4.2 域(Field)信息

域信息就是我们常说的类中的 成员变量/属性
在这里插入图片描述

5.4.3 方法信息

在这里插入图片描述

5.4.4 运行时常量池

在这里插入图片描述

5.4.5 字符串常量池

字符串常量池是储存字符串常量


5.5 方法区细节的演进

在这里插入图片描述


本文参考书目
《深入理解Java虚拟机》
本文参考网课
《尚硅谷宋红康》老师录制的深入理解JVM

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值