概述:

  对于从事C、C++开发的程序员来说,在内存管理领域,他们既是拥有最高权力的“皇帝”,又是从事最基础工作的劳动人民——既拥有每个对象的“所有权”,

又担负着每一个对象从开始到终结的维护职责。

  对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为没一个new操作去配对的free/delete(C、C++语言对对象的删除和内存释放操作),

不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切看起来很美好。不过,也正是java把控制内存的权力交给了java虚拟机,一旦出现内存泄漏

和内存溢出方面的问题,如果不了解虚拟机是怎么使用内存的,那排查错误、修正问题将会成为一项异常艰难的工作。

运行时数据区:

  java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有些区域会随着

虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。如下图所示:

我们知道JVM也属于一种特殊的操作系统,那这些数据区域跟我们最常用的windows哪些部分相对应呢。我们可以吧windows的CPU+缓存+主内存和JVM的执行引擎+

操作数栈+(栈、堆)对应起来,这样更加利于我们去理解JVM。

虚拟机栈:

  从上图可见,java虚拟机栈是线程私有的,它的生命周期和线程相同。虚拟机栈描述的是java方法执行的线程内存模型:每个方法被执行的时候,java虚拟机都会

同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、返回地址等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到

出栈的过程。我们来通过一段非常简短的代码来演示虚拟机栈的作用:

/**
* @ClassName StackTest
* @description:
* @author:liuyi
* @Date:2020/11/23 23:45
*/
public class StackTest { public static void main(String[] args) {
A();
} static void A(){
B();
} static void B(){
C();
} static void C(){ }
}

  当我们运行main方法,虚拟机会开启一个线程,同时为当前线程划分一块内存区域作为当前线程的虚拟机栈。同时在执行每个方法的时候都会打包成一个栈帧。

比如 main 开始运行,打包一个栈帧送入到虚拟机栈。C 方法运行完了,C 方法出栈,接着 B 方法运行完了,B 方法出栈、接着 A 方法运行完了,A 方法出栈,

最后 main 方法运行完了,main 方法这个栈帧就出栈了。这个就是 Java 方法运行对虚拟机栈的一个影响。虚拟机栈就是用来存储线程运行方法中的数据的。而

每一个方法对应一个栈帧。入栈过程如下图所示:

上图描述了整个main方法调用的入栈和出栈的过程,需要注意的是栈帧出栈之后就没了,栈帧没得GC的说法。

栈帧详解:

  栈帧大体都包含四个区域:(局部变量表、操作数栈、动态连接、返回地址)

  • 局部变量表:顾名思义就是局部变量的表,用于存放我们的局部变量的(方法中的变量)。首先它是一个 32 位的长度,主要存放我们的 Java 的八大基础数据
类型,一般 32 位就可以存放下,如果是 64 位的就使用高低位占用两个也可以存放下,如果是局部的一些对象,比如我们的 Object 对象,我们只需要存放它的一个引用地址即可。
  • 操作数栈:存放 java 方法执行的操作数的,它就是一个栈,先进后出的栈结构,操作数栈,就是用来操作的,操作的的元素可以是任意的 java 数据类型,所
以我们知道一个方法刚刚开始的时候,这个方法的操作数栈就是空的。操作数栈本质上是 JVM 执行引擎的一个工作区,也就是方法在执行,才会对操作数栈进行操作,如果代码不不执行,操作数栈其实就是空的。
  • 动态连接:Java 语言特性多态(后续章节细讲,需要结合 class 与执行引擎一起来讲)。
  • 方法出口:正常返回(调用程序计数器中的地址作为返回)、异常的话(通过异常处理器表<非栈帧中的>来确定)。

我们来通过分析一个简单的方法来理解栈帧中各个区域是如何运作的,代码如下:

/**
* @ClassName User
* @description:
* @author:liuyi
* @Date:2020/11/25 20:51
*/
public class User {
public static int work(){
int a = 2;
int b = 3;
int c = a*b;
return c;
}

public static void main(String[] args) {
System.out.println(work());
}
}

 当该程序运行的时候,JVM会为其分配虚拟机栈,并生成对应的栈帧,如下图所示:

我们通过反汇编命令查看work方法的字节码如下:

我们看到work方法一共由10条字节码组成,我们来逐步分析。

打开 https://cloud.tencent.com/developer/article/1333540查看字节码指令

现来看iconst_2对应的含义,如图

所以第1个字节码是将一个值为2的数字加载到操作数栈。再来看 istore_0的含义,如图

  所以第2个字节码的含义就是将第一步中放入到操作数栈的数字放到局部变量表中,位置为0。所以前面两个字节码对应的java代码就是int a = 2;那么显而易见3和4两个字节码对应的

就是int b = 3;到这里,大家心里肯定会有疑问,为什么不直接将值放到局部变量表呢?我们接着分析,你就明白了。

  继续来看第5和第6两个字节码:iload_0和iload_1,它们的含义是将局部变量表中位置0和1的两个数加载到操作数栈中,接着我们来看关键的第7个字节码:imul,它代表的意思

是相乘,就是将操作数栈中的数字进行乘法运算,我们知道相乘是需要运算的,所以此时要交给执行引擎运算,运算完成之后再将运算的结果返回到操作数栈。所以操作数栈的作用

就是为jvm高速的计算提供缓冲区。

  接着来看第8个字节码:istore_2,它的含义就是将计算的结果放入局部变量表,到这里int c = a*b;就执行完了。然后再来看第9和第10个字节码,它们的含义是将局部变量表的值再

压入操作数栈,最后返回。至此,整个方法执行结束,以上就是栈帧中各个区域在方法执行中的运作流程。

虚拟机栈大小的设置:

  虚拟机栈的大小缺省为 1M,可用参数 –Xss 调整大小,例如-Xss256k。

参数官方文档(JDK1.8):https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html。

我们可以看到linux的建议配置为1M,至于windows为啥没有,博主大胆猜想可能跟微软和Oracel两家公司竞争有关吧,毕竟微软开发.net就是和java竞争的。

虚拟机栈相关的程序异常:

  • StackOverflowError异常:如果线程请求的栈深入大于虚拟机所允许的深度,将抛出StackOverflowError异常,通常是由无线递归导致的,如下面的代码

  • OutOfMemoryError:如果java虚拟机的容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。这种情况基本很少出现,也很难模拟,这里就不演示了。

程序计数器:

  与虚拟机栈一样,程序计数器也是线程私有的。程序计数器是一块很小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,就如上面反汇编User.class看到的一样。每一个字节码都有自己的序号:

   如上图所示,虽然这些序号是由顺序的,但是并不一定是依次递增,如果某给字节码占用的空间很大,那么它的序号相较于前一个序号就差距更大。

在java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的执行器,分支、

循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。

  它还有另外一个作用,我们知道在java中可以开启成百上千个线程,但是我们一般的电脑CPU也就8个左右。java虚拟机的多线程是通过线程轮流切换、

分配处理器执行时间方式来实现的,那么切换后虚拟机是怎么知道以前运行的位置,继续运行的呢?这个时候,程序计数器就起到了决定性的作用,因为

程序计数器是线程独有的,所以不会相互影响,当切回到当前线程,根据程序计数器记录的序号,继续执行对应的字节码即可。

  在JVM中,只有执行java方法的时候,程序计数器才会记录正在执行的虚拟机字节码指令的地址,如果正在执行的是本地(Native)方法,这个计数器

则应为空(Undefined)。但是这里会产生一个疑问,如果刚好在执行Native方法的时候线程切换了,那切回来之后该怎么找到对应的位置呢?这里,我猜测

JVM可能规定了 在执行Native本地方法的时候,禁止切换当前线程(如不正确,请指正)。xianc

本地方法栈:

  本地方法栈与虚拟机栈的作用非常相似,其区别只是虚拟机栈为java方法服务,而本地方法栈专门为Native本地方法服务。需要注意的是,HotSpot直接把

本地方法栈和虚拟机栈合并了。

总结:

  本篇文章介绍了JVM的内存区域之线程私有区域,主要介绍了虚拟机栈的各个组成部分以及java方法是怎么通过虚拟机栈来实现执行的,接着介绍了程序计数器的作用

最后简述了本地方法栈。下一章,我们将要分析JVM内存区域的线程共享数据区,主要包括堆、方法区、运行时常量池以及直接内存等内容。

JVM(二)-内存区域之线程私有区的更多相关文章

  1. 一、JVM — Java内存区域

    Java 内存区域详解 写在前面 (常见面试题) 基本问题 拓展问题 一 概述 二 运行时数据区域 2.1 程序计数器 2.2 Java 虚拟机栈 2.3 本地方法栈 2.4 堆 2.5 方法区 2. ...

  2. JVM——Java内存区域

    一,概述: Java跟C++不同,在内存管理区域C++程序员拥有着最高权力,但是正是因为如此,所以C++程序员要照顾这个对象的生老病死,从创建到消亡都是由程序员决定的. 但是Java程序员在虚拟机的自 ...

  3. jvm的内存区域介绍

    什么是jvm? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的 ...

  4. 深入理解JVM - Java内存区域与内存溢出异常 - 第二章

    一 运行时数据区域 JVM在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间. 程序计数器 程序计数器(Program Counter ...

  5. JVM的内存区域划分

            JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的 ...

  6. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  7. 01 深入理解JVM的内存区域

    先来看看JVM运行时候的内存区域,如下图: 大多数 JVM 将内存区域划分为 Heap(堆).方法区.Stack(栈).本地方法栈.程序计数器.其中 Heap 和 方法区 是线程共享的,Stack.本 ...

  8. JVM的内存区域模型

    首先要明白一个概念,就是JVM的内存区域划分与java的内存区域模型是两个不同的概念,前者指的是在java中jvm会将一个程序划分为哪些块来存储对应的数据,后者是一个更宏观上的j概念,指的是java线 ...

  9. JVM的内存区域划分(转)

    原文链接:JVM的内存区域划分 JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内 ...

随机推荐

  1. 数据恢复软件推荐-easyrecovery绿色破解版(附注册码)免费下载

    easyrecovery破解版专注于PC端存储数据的抢救恢复,软件的整体界面风格和360杀毒有些许相似,没有看起来像牛皮藓的杂乱广告,只有六个功能按键,对应你所遇到的数据丢失状况级别,点击最为适合的功 ...

  2. 【应用服务 App Service】App Service证书导入,使用Key Vault中的证书

    问题描述 正常情况下,如果需要为应用服务安装SSL证书,可以在证书准备好的情况,通过门户上传即可,详细步骤可以参考微软官方文档(在 Azure 应用服务中添加 TLS/SSL 证书:https://d ...

  3. golang1.16内嵌静态资源指南

    今天是万圣节,也是golang1.16新特性冻结的日子.不得不说自从go2路线发布之后golang新特性的迭代速度也飞速提升,1.16中有相当多的重要更新,包括io标准库的重构,语言内置的静态资源嵌入 ...

  4. CodeForces 1419F Rain of Fire

    题意 不想写. 题解 场上想了 1h+ 无果,一到场外就口胡出来了,我真是个 sb. 首先注意到如果 \(t\) 满足条件那么 \(t+1\) 也会满足,所以答案具有单调性,可以二分,于是现在只需要考 ...

  5. 「MCOI-03」村国题解

    第二篇题解! 可能是退役之前的最后一篇题解了 (好像总共都只写了两篇) 不说了,讲题: 题面 题意: 有T个数据 有一颗树(保证所有的的节点都是相连的),有n个节点,每个节点都有相应的权值与序号,现在 ...

  6. python实现城市气候与海洋的关系研究

    城市气候与海洋的关系研究 关注公众号"轻松学编程"了解更多. 以下命令都是在浏览器中输入. cmd命令窗口输入:jupyter notebook 后打开浏览器输入网址http:// ...

  7. 热部署只知道devtools吗?JRebel不香吗?

    持续原创输出,点击上方蓝字关注我 目录 前言 JRebel收费怎么破? 什么是本地热部署? 什么是远程热部署? JRebel和devtools的区别 如何安装JRebel? 如何本地热部署? 如何远程 ...

  8. 我叫MongoDb,不懂我的看完我的故事您就入门啦!

    这是mongo基础篇,后续会连续更新4篇 大家好我叫MongoDb,自从07年10月10gen团队把我带到这个世界来,我已经13岁多啦,现在越来越多的小伙伴在拥抱我,我很高兴.我是NoSQL大家族的一 ...

  9. 参悟python元类(又称metaclass)系列实战(一)

    写在前面 之前在看廖雪峰python系列的教程时,对元类的章节一直头大,总在思考我到底适不适合学习python,咋这么难,尤其是ORM的部分,倍受打击:后来从0到1手撸了一套ORM,才稍微进阶了一点理 ...

  10. 一年前,我来到国企搞IT

    ​ 2020.11.01日,这一天是我加盟xxx国企的一年整,这篇分享本来是要提前写的,不过由于前段时间确实繁忙,一直没有机会提笔.今天简单和大家分享下我在国企的一些工作内容,感悟等等,希望能给那些对 ...