【JVM第三篇--运行时数据区】程序计数器、虚拟机栈、本地方法栈
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记。其观看地址如下:尚硅谷2020最新版宋红康JVM教程
一、运行时数据区
我们在编写Java程序时,使用JVM的流程主要如下所示:
虚拟机在执行Java程序时,会把它所管理的内存划分为不同的数据区域,即运行时数据区。有些数据区域是线程共享的,即这些区域会随着虚拟机的启动而创建,随着虚拟机的关闭而销毁。而另一些区域则是与线程对应,属于线程私有的。这些区域会随着线程开始而创建,随着线程的结束而销毁。
具体的划分如下:
多个线程共享的:堆、方法区
每个线程私有的:程序计数器、Java虚拟机栈、本地方法栈
图示如下:
关于Java线程:在虚拟机中,每个线程都与操作系统的本地线程是直接对应的。当Java的线程准备执行时,操作系统的线程也同时创建了。
二、程序计数器
程序计数器是一块很小的内存空间,可以认为是当前线程所执行的字节码的行号指示器,负责保存当前线程正在执行的字节码地址。
1、程序计数器的作用
① Java是支持多线程的,这也意味着CPU会不停地切换线程。虚拟机的多线程是通过CPU时间片轮转法来实现的,即每个线程占用CPU的时间是同等的,时间一到就会切换线程,如此重复,直到线程终止。这也意味着某个线程会因为时间片到而被挂起,所以当该线程再次获得时间片时,需要知道从哪个地方继续执行,此时虚拟机只需要读取程序计数器的值就可以知道要执行的字节码指令的位置了。这也是程序计数器是线程私有的原因,因为若程序计数器不是线程私有的话,当CPU切换线程时,会按照上一个线程的字节码指令的位置来执行当前线程的字节码指令,这显然是不正确的
② JVM的字节码解释器也需要通过改变程序计数器的值来明确下一条字节码指令。
2、程序计数器的特点
① 程序计数器是数据区中唯一没有规定OutOfMemoryError
(内存溢出异常)的区域。
②如果一个线程执行的是Native本地方法,那么程序计数器的值为undefined。因为JVM在执行Native本地方法时,是通过JNI调用本地其他语言来实现的,而不是字节码。
三、虚拟机栈
栈是运行时的单位,堆是存储的单位。
栈解决程序的运行问题,即方法如何执行(或者如何处理数据)。
堆解决的是数据存储问题,即数据怎么放,在那放。
1、什么是虚拟机栈
虚拟机栈在每个线程开始时随之创建,虚拟机栈与数据结构中的栈一致,也是遵循先进后出的原则。
虚拟机栈进行出栈入栈操作的元素就是栈帧。每一个栈帧就对应着一个方法,也可以将栈帧理解为一个方法的运行空间。
一个栈帧入栈,意味着一个方法被调用,一个栈帧出栈,即一个方法执行完毕。则栈帧的入栈顺序就是方法的调用顺序。
有代码:
public class VMStackTest {
private static int i = 1;
public static void main(String[] args) {
VMStackTest test = new VMStackTest();
test.add();
System.out.println(i);
}
public void add(){
i++;
}
}
代码所示的虚拟机栈示例如下:
同一时刻,在同一线程中,只有位于虚拟机栈顶部的栈帧才是有效运行的,即只有位于栈顶的方法是正在执行的。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。 如果在被调用的A方法中,又调用了B方法,那么对应的B方法的栈帧就会被创建,并被放在虚拟机栈的栈顶,成为新的正在执行的方法。
对于虚拟机栈来说,不存在垃圾回收问题。
Java方法有两种方法返回方式,一种是正常的方法返回,使用return指令;另一种是抛出异常。不管是哪一种,都会导致栈帧被弹出。
2、栈帧的内部结构
每个栈帧都存储着:
① 局部变量表
②操作数栈
③方法返回地址
④动态链接
⑤一些附加信息
图示如下:
(1)、局部变量表
局部变量表定义为一个数字数组,用于存放方法参数和方法内定义的局部变量。
局部变量表建立在线程的虚拟机栈上,是线程的私有数据,因此不存在数据安全问题。
虚拟机通过索引定位的方法查找相应的局部变量,索引的范围是从0~局部变量表最大容量。
局部变量表所需的容量大小是在编译期定下的,方法运行期间是不会该变局部变量表的大小的。
(2)、操作数栈
操作数栈也是一个栈的数据结构,操作数栈的最大深度也在编译的时候定下。
操作数栈的每一个元素可以是任意Java数据类型,在方法执行过程中,操作数栈的深度都不会超过最大值。
操作数栈的作用是: 随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。
(3)、方法返回地址
一个方法结束,有正常执行完毕退出和抛出异常(没catch),非正常退出两种。
无论是那种方式,在退出后都会返回到该方法被调用的位置。
正常退出时,调用者的程序计数器的值会作为返回地址,即调用该方法的指令的下一条指令的地址。但是异常退出时,返回地址要通过异常表来确定。
实际上,方法的退出就是当前栈帧出栈的过程。故需要恢复上一层栈帧的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈,并设置其程序计数器的值等,让调用者栈帧继续执行下去。
两种方法结束的区别是:因抛出异常而退出的方法不会给调用他的调用者栈帧任何返回值
3、虚拟机栈的常见异常
《Java虚拟机规范》中,允许虚拟机栈的大小是动态扩展的或者固定不变的。
这意味着,虚拟机栈会出现两种异常:
① StackOverflowError
异常(栈溢出):
如果线程请求分配的栈容量超过Java虚拟机的最大容量时,就会抛出栈溢出异常。最简单的示例就是不停递归而不退出,就会报 StackOverflowError
。
当虚拟机栈的大小为固定时,易出现该异常。
②OutOfMemoryError
异常(内存溢出)
当虚拟机栈采用动态扩展时,一定程度上可以规避 StackOverflowError
,但是虚拟机给每个线程分配到的内存空间是有限的,这也意味着隶属于线程的虚拟机栈的内存也是有限的,所以当虚拟机栈请求扩展的内存大小无法满足时,就会报该异常。
但是并不意味着,采用固定大小的虚拟机栈就不会报该异常,如果某个栈帧太大,超出虚拟机栈所有的内存也是可能的。
四、本地方法栈
(1)、本地方法
一个Native Method就是一个Java调用非Java代码的接口。在定义一个本地方法时,并不提供实现体(有些就像定义一个Java interface),因为实现体是由非Java语言在外面实现的。本地接口的作用是融合不同的编程语言为Java所用。
(2)、本地方法栈
虚拟机栈是用于管理Java方法的调用,本地方法栈是用于管理本地方法的调用。
①本地方法栈是线程私有的
②本地方法栈也是可实现固定或者可动态扩展的内存大小(这也意味着本地方法栈的异常也与虚拟机栈一致)
③本地方法栈存放着被native关键字标记的方法,在执行引擎执行时加载本地方法库。
④并不是所有的JVM都支持本地方法,故也无需实现本地方法栈
⑤在HotSpot JVM中,直接将本地方法和虚拟机栈合二为一。
五、扩展--设置栈内存大小
有代码如下:
public class StackOverError {
private static int count = 1;
public static void main(String[] args) {
System.out.println(count);
count++;
main(args);
}
}
在代码中采用了递归,以count++
的次数来查看目前虚拟机栈的深度。其结果如下:
可见默认情况下,count
的值为2467。
我们可以使用
-Xss:来规定了每个线程虚拟机栈的内存大小
如下,
设置栈的大小:--Xss1m或者-Xss1k (设置方法IDEA:Run --> Edit Configuration --> VM option)
结果如下,
可见当设置虚拟机栈的内存大小为2m时,count
的值为233424。
故可看出使用参数 -Xss可以设置线程的最大栈空间,且虚拟机栈的大小直接决定了函数调用的大小。
【JVM第三篇--运行时数据区】程序计数器、虚拟机栈、本地方法栈的更多相关文章
- JVM 专题九:运行时数据区(四)本地方法栈
1. 本地方法栈 2. 什么是本地方法栈? Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用 本地方法栈,也是线程私有的. 允许被实现成固定或者是可动态拓展的内存大小 ...
- 【JVM第四篇--运行时数据区】堆
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.堆的概述 JVM的运行时数据区如下: 一个Java程序运行起来对应着一个进程(操 ...
- 【JVM第五篇--运行时数据区】方法区
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.栈.堆.方法区的关系 虚拟机运行时的数据区如下所示: 即方法区是属于线程共享的内 ...
- JVM详解(三)——运行时数据区
一.概述 1.介绍 类比一下:红框就好比内存的运行时数据区,在各自不同的位置放了不同的东西.而厨师就好比执行引擎. 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的 ...
- JVM 专题十:运行时数据区(五)堆
1. 核心概述 1.1 堆概述 一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器.本地方法栈和虚拟机栈. 一个jvm实例只存在一个堆内存 ...
- JVM运行时数据区--Java虚拟机栈
虚拟机栈的背景 由于跨平台性的设计,java的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 根据栈设计的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样 ...
- jvm入门及理解(三)——运行时数据区(程序计数器+本地方法栈)
一.内存与线程 内存: 内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了JAVA在运行过程中内存申请.分配.管理的策略,保证了JVM的 ...
- JVM 专题八:运行时数据区(三)虚拟机栈
2.虚拟机栈 1. 概述 1.1 虚拟机栈出现背景 由于跨平台性的设计,java的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 优点是跨平台,指令集小,编译器容易实现, ...
- JVM系列之四:运行时数据区
1. JVM架构图 Java虚拟机主要分为五大模块:类装载器子系统.运行时数据区.执行引擎.本地方法接口和垃圾收集模块. 2. JDK1.7内存模型-运行时数据区域 根据<Java 虚拟机规范( ...
随机推荐
- 【暑假集训】HZOI2019 水站 多种解法
题目内容 已知有一个\(n\)层的水站: \(W_i\)表示未操作之前第\(i\)层的已有水量: \(L_i\)表示第\(i\)个水站能够维持或者储存的水的重量: 表示在第\(P_i\)层进行减压放水 ...
- frp 内网穿透远程桌面(Windows 10)配置
一.服务端配置 服务端需要公网环境,一般用一台云服务器就行了,我选择的是 Linux 服务器,Windows 服务器也是可以的. 下载 frp: wget https://github.com/fat ...
- naicat如何查看表关系
1.navict版本为:navict premium https://www.php.cn/tool/navicat/427617.html 参考上面链接 2. 这个版本的 目前就这些,后续进行补充
- mybatis-plus自动填充
1,给字段添加注解 @TableField(value = "create_time", fill = FieldFill.INSERT) 2,添加填充处理器,需要实现接口Meta ...
- revel run报错 undefined: sys call.SIGUSR2"
revel run报错,报错信息为 o Compilation Error (in ..\\..\\revel\\server_adapter_go.go:135): undefined: sysca ...
- 第三章 MySQL的多实例
一.MySQL服务构成 1.MySQL程序结构 1.连接层 2.sql层 3.存储引擎层 2.MySQL逻辑结构 1.库 2.表:元数据+真实数据行 3.元数据:列+其它属性(行数+占用空间大小+权限 ...
- 显示器类型对美乐威NDI IP转换器延时影响测试
背景 用户在选择用网络传输视频时,传输延迟通常是他们非常关心的数据.集成商在探究如何降低视频传输延时,往往专注于网络本身和视频编码的优化,容易忽略视频解码和播出也是整个视频传输过程中非常重要的环节.本 ...
- eclipse之SSH配置spring【二】
第一篇中配置struts完成(http://www.cnblogs.com/dev2007/p/6475074.html),在此基础上,继续配置spring. web.xml中增加listener,依 ...
- 【转】Loading PNGs with SDL_image
FROM:http://lazyfoo.net/tutorials/SDL/06_extension_libraries_and_loading_other_image_formats/index2. ...
- Kubernetes Pod驱逐策略
Kubelet 能够主动监测和防止计算资源的全面短缺. 在资源短缺的情况下,kubelet 可以主动地结束一个或多个 Pod 以回收短缺的资源. 当 kubelet 结束一个 Pod 时,它将终止 P ...