JVM函数调用:Java出入栈

JVM函数调用:Java出入栈

目录

局部变量表

索引复用

垃圾回收

栈数据区

栈上分配

线程作为系统运算调度的最小单位,在JVM中线程的行为体现就是函数调用,函数调用中数据的传递就是通过Java栈,Java栈顾名思义有着和数据结构中“栈”相似的属性,后进先出,出栈入栈,栈中保存的是栈帧,当JVM发生函数调用时,就会有一个栈帧被压入Java栈,当函数调用结束后,再从栈中弹出栈帧,当前正在执行的函数其对应的栈帧位于栈顶处,且保存有当前函数的局部变量表和栈数据区(保存一些中间结果等数据)。在函数返回,也就是有栈帧要从Java栈中弹出时,正常的情况是函数通过return返回,此时栈帧正常弹出,如果函数调用出现问题无法正常返回,则抛出异常,举个例子:我们每一次函数调用时都会对Java栈进行入栈操作,栈空间是一定的,随着不断入栈操作,例如递归函数调用,栈空间变得越来越小,最后达到最大可用深度时,就会抛出栈溢出异常,所以有时我们递归函数调用过程中出现的“StackOverflowError”,就是栈空间因为某些原因被占满了导致的。

局部变量表

函数对应的栈帧中有一个局部变量表,里面保存了调用函数的局部变量,参数等,这些参数和变量是跟着函数走的,只在当前函数调用中有效,函数调用结束后,栈帧就会弹出Java栈,局部变量表也就随之被销毁。来看一个简单的例子:

draw()方法中有3个入参和3个局部变量,它们都是int数据类型,一个占用24个字节内存空间,在32位操作系统中每4个字节为一个字,所以在局部变量表中,函数draw()的局部变量一共占6个字。

从上图可以看到,Maximum local variables表示最大局部变量表大小为6个字。

索引复用

详细点开draw()函数中的局部变量表,可以看到一些更详细的属性,例如每一个变量的索引index,变量名name和数据类型descriptor等。

局部变量表中的索引是可以进行复用的,以次来节省Java栈空间,具体的复用方式是这样,假设我们定义了一个局部变量i,当程序运行到i离开其作用域后,再定义的其他变量可能就会复用i变量的索引,具体看个例子:

package cell;

public class Cell {

private static boolean tag = true;

public void example1() {

int i = 9527;

System.out.println("i = " + i);

int s = 9527;

}

public void example2() {

if (tag) {

int i = 9527;

System.out.println("i = " + i);

}

int s = 9527;

}

}

程序中有两个方法,example1()中定义的变量i和变量s作用域都是一样的,直到example1()方法的结束,所以两者的索引没有办法复用。example2()方法中,局部变量i在第16行后就离开了作用域范围,那么后续定义的局部变量s可以复用它的索引,从局部变量表里看,的确是这样的:

可以看到,方法example1()中局部变量i和s的所有不同,分别是1和2,而到了方法example2()中,局部变量i和s的所有都一样是1。

垃圾回收

索引复用有时也会对JVM的垃圾回收产生影响,例如某一个变量i,虽然离开了自己的作用域,但是它之前指向了某一对象,使得变量i仍然存在于局部变量表中,导致变量i指向的对象无法被回收如果变量i指向了某一对象,i离开了作用域后,其索引被后面定义的局部变量所服用了,那么变量i也会被销毁掉,其指向的对象也就能被正常GC,看一个例子:

public void example1() {

byte[] buffer = new byte[2*1024*1024];

System.gc();

}

public void example2() {

byte[] buffer = new byte[2*1024*1024];

buffer = null;

System.gc();

}

public void example3() {

boolean tag = true;

if (tag) {

byte[] buffer = new byte[2*1024*1024];

}

System.gc();

}

public void example4() {

boolean tag = true;

if (tag) {

byte[] buffer = new byte[2*1024*1024];

}

int i = 9527;

System.gc();

}

上面的程序中,4个方法,第一个example1()中,首先定义byte数组,申请2MB大小的空间,之后立刻进行GC,回收该数组对象,但是注意,此时因为局部变量buffer强引用了这块内存空间,所以gc暂时无法对其进行回收。到example2(),在为byte数组申请空间,并用局部变量buffer引用这块区域后,显示将buffer置为null,这样buffer就不会强引用这块内存空间,之后再进行GC就可以成功回收。example3()中,我们让局部变量buffer作用在if语句中,当if语句结束,buffer离开了它的作用域后,我们再进行GC,此时因为变量buffer还存在于局部变量表中,所以它的引用还是有效的,GC无法对其引用的空间进行回收,解决的办法来看example4()方法。在example4()中,buffer离开其作用域后,我们再声明另一个局部变量i,让它来复用变量buffer的索引,,这样buffer变量就真正被替代销毁了,并且没有其他变量引用这片内存区域,之后再进行GC,可以成功进行回收。

栈数据区

Java栈中局部变量表上面简单总结了一下,除了局部便变量表,还有栈数据区,分为虚拟机栈和本地方法栈,虚拟机栈存放就是栈帧,Java方法运行时所需要的数据,本地方法栈存放的是JVM调用的本地方法。

栈上分配

上面说的栈数据区中,虚拟机栈和本地方法栈都是线程独占的,对于一些线程私有的,不能被其他线程访问的对象,JVM可以把它们分配在栈上,这样当函数调用结束后就会自行出栈销毁,不需要GC来进行回收,好处就是提高了性能。

private static People p;

public static void initPeople() {

p = new People();

p.name = "Alex";

p.age = 20;

}

上面代码中People类对象p是一个逃逸的对象,因为它是类的成员变量,可能会被其他线程访问到,所以虚拟机会把它分配到堆上,而不是线程私有的栈数据区中。如果我们把它改成非逃逸的对象:

public static void initPeople() {

People p = new People();

p.name = "Alex";

p.age = 20;

}

把对象p改成局部变量的方式,且initPeople()方法也没有将其返回出去,那么该对象p没有发生逃逸,虚拟机就会将它分配到栈数据区中。

JVM函数调用:Java出入栈相关教程

java进出栈_JVM函数调用:Java出入栈相关推荐

  1. java 类 模型_JVM之Java对象模型

    oop-klass klass 在JVM中,Klass是用来描述一个类的元数据的,里面包括了类的修饰符.类名.父类.类加载器等,简单来说就是描述一个Java类的元数据的数据结构. class Klas ...

  2. linux查看java虚拟机内存_JVM:查看java内存情况命令

    jmap (linux下特有,也是很常用的一个命令) 观察运行中的jvm物理内存的占用情况. 参数如下: -heap :打印jvm heap的情况 -histo: 打印jvm heap的直方图.其输出 ...

  3. java 命令 线程栈_JVM调试常用命令——jstack命令与Java线程栈(1)

    1 jstack 命令 jstack命令的主要作用是打印指定Java进程中每一个线程的工作状态,以及每个线程栈当前的方法执行顺序等详细情况.为什么jstack命令不和jmap.jinfo.jstat等 ...

  4. java 本地方法栈_JVM学习笔记-本地方法栈(Native Method Stacks)

    本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native ...

  5. java gc回收堆还是栈_浅析JAVA的垃圾回收机制(GC)

    1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制. 注意:垃圾回 ...

  6. linux java 栈_关于Java中栈与堆的思考

    1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2. 栈的优势是,存取速度比堆要快,仅次于直接位于C ...

  7. Leetcode刷题 225题:用队列实现栈(基于Java和c++两种语言)

    ** Leetcode刷题 225题:用队列实现栈(基于Java和c++两种语言) ** 题目: 使用队列实现栈的下列操作: push(x) – 元素 x 入栈 pop() – 移除栈顶元素 top( ...

  8. 《LeetCode力扣练习》剑指 Offer 09. 用两个栈实现队列 Java

    <LeetCode力扣练习>剑指 Offer 09. 用两个栈实现队列 Java 一.资源 题目: 用两个栈实现一个队列.队列的声明如下,请实现它的两个函数 appendTail 和 de ...

  9. java 数据结构实例_数据结构(Java)——栈的实例

    惟大英雄能本色,是真名士自风流 --易中天(百家讲坛) 1.表达式的转换 1.1 中缀表达式转前缀表达式 中缀表达式转前缀表达式有许多的方式,有加括号去除法.语法树遍历法.堆栈处理法1. 测试程序的实 ...

最新文章

  1. python多包运行_如何组织包含多个包的python项目,以便包中的每个文件仍然可以单独运行?...
  2. 在Mac OS环境下安装MySQL服务
  3. 怎样将python的文件转化为windows的可执行程序
  4. Spring核心部分之AOP,aspectJ框架实现AOP,切入点表达式
  5. 巡检水中机器人_物联卡的应用,管廊隧道巡检机器人“上岗”啦!
  6. 集成hello到OpenDaylight发行版中
  7. Intel-VT 与虚拟化限制
  8. GTRD:最全面的人和小鼠转录因子chip_seq数据库
  9. 使用qBittorrent下载bt种子文件
  10. 解读《美国国家BIM标准》– BIM能力成熟度模型 (一)
  11. c++复习篇(三)--函数调用堆栈
  12. SAP 月末结账步骤
  13. SAN存储和服务器虚拟化安装方案,ESX/ESXi虚拟化系统与SAN存储结构结合的配置安装流程...
  14. STM32 SHT10温湿度传感器的信号采集
  15. 范蠡传(司马迁-史记)
  16. 设计模式 -- 访问者模式(Visitor)
  17. 用Python写了一个网易云音乐(附源码、视频教程)
  18. filtering and hybrid images
  19. 调研分析:全球与中国多媒体投影仪镜头市场现状及未来发展趋势
  20. 我奋斗了18年才和你坐在一起喝咖啡 原作者:麦子

热门文章

  1. 1、SELECT:数据表查询语句
  2. 1、Math类的常用方法
  3. A. Di-visible Confusion【思维】
  4. 【PAT乙级】1005 继续(3n+1)猜想 (25 分)
  5. 1.2操作系统的特征
  6. JavaScript的文档窗口事件
  7. Java之JDK和JRE
  8. 一个Github项目搞定微信、QQ、支付宝等第三方登录
  9. 笔记本8G+256G固态免费送,吃鸡不吃力,包邮!
  10. 蓝桥杯-队列操作(java)