写在前面的话:本文是在观看尚硅谷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虚拟机栈执行原理深入详解

2020最新-JVM-Java虚拟机-从入门到精通-尚硅谷

知乎-Java虚拟机—栈帧、操作数栈和局部变量表

【JVM第三篇--运行时数据区】程序计数器、虚拟机栈、本地方法栈的更多相关文章

  1. JVM 专题九:运行时数据区(四)本地方法栈

    1. 本地方法栈 2. 什么是本地方法栈? Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用   本地方法栈,也是线程私有的. 允许被实现成固定或者是可动态拓展的内存大小 ...

  2. 【JVM第四篇--运行时数据区】堆

    写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.堆的概述 JVM的运行时数据区如下: 一个Java程序运行起来对应着一个进程(操 ...

  3. 【JVM第五篇--运行时数据区】方法区

    写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.栈.堆.方法区的关系 虚拟机运行时的数据区如下所示: 即方法区是属于线程共享的内 ...

  4. JVM详解(三)——运行时数据区

    一.概述 1.介绍 类比一下:红框就好比内存的运行时数据区,在各自不同的位置放了不同的东西.而厨师就好比执行引擎. 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的 ...

  5. JVM 专题十:运行时数据区(五)堆

    1. 核心概述 1.1 堆概述 一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器.本地方法栈和虚拟机栈. 一个jvm实例只存在一个堆内存 ...

  6. JVM运行时数据区--Java虚拟机栈

    虚拟机栈的背景 由于跨平台性的设计,java的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 根据栈设计的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样 ...

  7. jvm入门及理解(三)——运行时数据区(程序计数器+本地方法栈)

    一.内存与线程 内存: 内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了JAVA在运行过程中内存申请.分配.管理的策略,保证了JVM的 ...

  8. JVM 专题八:运行时数据区(三)虚拟机栈

    2.虚拟机栈 1. 概述 1.1 虚拟机栈出现背景 由于跨平台性的设计,java的指令都是根据栈来设计的.不同平台CPU架构不同,所以不能设计为基于寄存器的. 优点是跨平台,指令集小,编译器容易实现, ...

  9. JVM系列之四:运行时数据区

    1. JVM架构图 Java虚拟机主要分为五大模块:类装载器子系统.运行时数据区.执行引擎.本地方法接口和垃圾收集模块. 2. JDK1.7内存模型-运行时数据区域 根据<Java 虚拟机规范( ...

随机推荐

  1. 如何从0到1的构建一款Java数据生成器-第一章

    前提 某天晚上老夫在神游时,想起白天公司同事说起的问题,这老表抱怨使用mysql生成大批的随机测试数据太过麻烦,问大家有没有好的工具推荐,老夫对这种事情当然不关心,毕竟我也不知道. 秉承着不懂就要问, ...

  2. 本地ssh快速登录 ssh免密登录

    每次登录都要ssh -p wang@xx.xx.xx.xx 虽然做了公钥验证 https://www.cnblogs.com/php-linux/p/10795913.html 不需要输入密码,但是每 ...

  3. Python-selenium显示等待

    #coding=utf-8 from selenium import webdriver from selenium.webdriver.common.by import By from seleni ...

  4. Python包安装及使用指南

    这里长期更新一些Python第三方包的安装教程,以及使用教程... Pygame 安装教程: Windows: 首先,查看已安装的Python版本:访问https://www.lfd.uci.edu/ ...

  5. C#之txt的数据写入

    一.背景 小伙伴们在使用C#开发时,可能需要将一些信息写入到txt,这里就给大家介绍几种常用的方法. 二.思路 2.1将由字符串组成的数组写入txt 此种方法不需要使用Flush和Close(). 如 ...

  6. Java POST请求案例

    <<<<<<<<<<<<<<<<<<<<<<<<< ...

  7. Windows下显示已经连过的Wifi密码

    Windows下显示已经连过的Wifi密码   1. 打开cmd   2. netsh   3. wlan show profile 可以得到所有已经连过的wifi   4. wlan show pr ...

  8. jacoco-1-java代码测试覆盖率之本地环境初体验

    前言 jacoco是一个开源的覆盖率工具,它针对的开发语言是java,其使用方法很灵活,可以插桩到Ant.Maven中,可以使用其JavaAgent技术监控Java程序等. 那么本次主要使用对java ...

  9. web应用部署(Tomcat,springboot部署方式)

    转载自:https://www.cnblogs.com/haimishasha/p/10791454.html 核心内容 1.在Tomcat中有四种部署Web应用的方式,分别是: (1)利用Tomca ...

  10. 基于PHP实现短信验证码接口的方法

    步骤: 1.登录荣联运通讯注册获取ACCOUNT SID.AUTH TOKEN.Rest URL(生产).AppID(默认): 2.注册测试用手机号码(先注册测试号码方可使用): 3.下载demo示例 ...