java局部变量表是栈帧重要组中部分之一。他主要保存函数的参数以及局部的变量信息。局部变量表中的变量作用域是当前调用的函数。函数调用结束后,随着函数栈帧的销毁。局部变量表也会随之销毁,释放空间。

由于局部变量表存在栈帧中。所以,如果函数参数和局部变量比较多,会使的局部变量表膨胀,每一次调用会占用更多的栈空间。最终结局就是栈空间内存一定的情况下调用的次数减少。

1.1.1. 局部变量表变量影响

下面的代码演示在栈空间内存一定的情况下,参数以及局部变量的大小对函数调用次数的影响。第一个函数recursion()不包含任何的参数和局部变量,第二个函数recursion()包含3个参数和4个局部变量,因此我们也可以算出局部变量表中包含了13个变量信息。第一个局部变量表拥有更深的调用层次。代码如下:

private static int count=0;
public static void recursion(int a,int b,int c){
long l1=12;
short sl=1;
byte b1=1;
String s="1";
System.out.println("count="+count);
count++;
recursion(1,2,3);
}
public static void recursion(){
System.out.println("count="+count);
count++;
recursion();
}

使用jvm参数-Xss128K执行上面第一个无参的recursion()函数,输出结果如下:

count=4495
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)

使用jvm参数-Xss128K执行上面第二个个有参的recursion()函数,输出结果如下:

count=3865
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:564)
at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:619)

可以得出结论:

在同等的栈容量下,局部变量少的函数可以支持更深的函数调用。调用次数也就越多。

如何证明结论是正确的呢?在这里我们借助jclasslib工具来查看局部变量表的局部变量信息。

下图显示了无参的recursion()函数最大的局部变量表大小为0个字。有参的recursion()函数最大的局部变量表大小为8个字。在这里说明一下:

int、short、byte、对象引用等占用一个字。long和double在局部变量中需要占用2个字。

(字 一个字在32位计算机中为4个字节的长度)

我们来算一下recursion(1,2,3);方法局部变量表的大小。

三个参数为int
所以 是3

long l1=12; 2

short sl=1; 1

byte b1=1; 1

String s="1"; 1

所以说 一共是8个字。

需要强调的一点是这里说的局部变量表指的是java栈空间的一部分,不要跟下面说的classs字节码中的局部变量表混淆。

下面展示class字节码中的局部变量表的内容:

从图中看以看出来一些信息:

在Class文件的局部变量表定义中,显示了每一个局部变量的作用范围、所在槽位的索引信息(index列信息),

变量的名称(name列)和数据类型信息(descriptor列)

数据类型信息映射:

I---int类型

D--double类型

B-byte类型

Ljava/lange/Integer --Integer类型

Ljava/lange/String--String类型

S--short类型

栈帧中的局部变量的槽位是可以重复使用的。如果一个声明的变量过了其作用域,那么其作用域之后申请的变量有可能复用过期的局部变量的槽位,从而能够达到节省资源目的。

1.1.2. 局部变量表槽位的复用

下面得代码显示了局部变量表槽位的复用。localVar1()函数中,局部变量a和b得范围都是到了函数的末尾所以b是没有办法复用a的卡槽所在的位置。localVar2()函数中,局部变量a在}之后不在有效了,所以b是可以复用a的卡槽的位置都是int类型所以是1个字。程序如下所示:

public void localVar1(){

int a=0;

System.out.println(a);

int b=0;

}

public void localVar2(){

{

int a=0;

System.out.println(a);

}

int b=0;

}

使用jclasslib工具来查看局部变量表的局部变量localVar1()信息如下图:

该函数最大的局部变量大小3个字,卡槽0位为thsi引用(实例方法的第一个局部变量都是this),第一个卡槽位变量为a,第二个卡槽位变量为b,每个变量是1个字所以一共是三个字。

使用jclasslib工具来查看局部变量表的局部变量localVar2()信息如下图:

该函数最大的局部变量大小2个字,卡槽0位为thsi引用(实例方法的第一个局部变量都是this),第一个卡槽位变量为a,第二个卡槽位变量为b,每个变量是1个字
但是b变量复用了a卡槽位所以一共是2个字。

局部变量表也是作为垃圾回收gc的重要参考点,只要被局部变量表中直接或者间接引用的对象都不会被回收。所以必须要理解局部变量表才能理解gc回收机制。

下面的主要讲解说明局部变量对垃圾回收的影响。

1.1.3. 局部变量对垃圾回收的影响

程序代码如下所示:

jvm参数-XX:+PrintGC参数 垃圾回收前后堆得大小

JvmTestLocalVarGc
t=new JvmTestLocalVarGc();

t.localvarGc1();

结果输出:[Full GC 3875K->3546K(15872K), 0.0050719 secs]

在申请空间后,立即调用GC垃圾回收,很明显,由于byte被b强引用所以无法回收这块空间。

JvmTestLocalVarGc
t=new JvmTestLocalVarGc();

t.localvarGc2();

结果输出:[Full GC 3875K->474K(15872K), 0.0036066 secs]

在垃圾回收前,现将b设置为null,使byte数组拾取引用,所以GC后byte数组被直接垃圾回收了。

JvmTestLocalVarGc t=new JvmTestLocalVarGc();

t.localvarGc3();

结果输出:[Full GC 3875K->3546K(15872K), 0.0069622 secs]

在进行垃圾回收前,先使局部变量b实现,虽然b离开了作用域,但是变量b亦然存放在局部变量表中。并且指向byte数组,故byte数组亦然没有被回收。

JvmTestLocalVarGc
t=new JvmTestLocalVarGc();

t.localvarGc4();

结果输出:[Full GC 3875K->474K(15872K), 0.0037666 secs]

在垃圾回收前,不仅是b失效了,c复用了变量b的字,由于b被销毁,所以byte数组被销毁了。

JvmTestLocalVarGc
t=new JvmTestLocalVarGc();

t.localvarGc5();

结果输出:[Full GC 3875K->3546K(15872K), 0.0054367 secs]

[Full GC 3546K->474K(15936K), 0.0036164 secs]

对于localvarGc5()调用localvarGc1()方法,很明显localvarGc1()中没有回收byte数组,但在其返回后他的栈帧被销毁了,自然栈帧中所有的局部变量也没销毁了,容器没了,值当然也不存在了嘛。

所以byte数组失去饮用。在localvarGc5()中被回收了。

java虚拟机 jvm 局部变量表实战的更多相关文章

  1. java虚拟机 jvm 方法区实战

    和java堆一样,方法区是一块所有线程共享的内存区域,用于保存系统的类信息,类的信息有哪些呢.字段.方法.常量池.方法区也有一块内存区域所以方法区的内存大小,决定了系统可以包含多少个类,如果系统类太多 ...

  2. java虚拟机 jvm 出入java栈 栈空间内存分配

    java栈空间是一块线程私有的内存空间,java堆和程序数据密切相关,那么java栈就是和线程执行密切相关.线程最基本的执行行为就是函数的调用.每次函数调用其实是通过java栈传递数据的. 数据结构中 ...

  3. 深入理解JAVA虚拟机JVM

    深入理解JAVA虚拟机JVM Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制.java之所以能实现一次编写到处执行,也就是因为jVM.原理:编 ...

  4. Java虚拟机(JVM)知多少

    本文大量参考:https://www.cnblogs.com/lfs2640666960/p/9297176.html 概述 JVM是JRE的一部分.它是一个虚构出来的计算机,是通过在实际的计算机上仿 ...

  5. 深入理解java虚拟机JVM(上)

    深入理解java虚拟机JVM(上) 链接:https://pan.baidu.com/s/1c6pZjLeMQqc9t-OXvUM66w 提取码:uwak 复制这段内容后打开百度网盘手机App,操作更 ...

  6. 深入解析java虚拟机-jvm运行机制

    转自oschina 一:JVM基础概念 JVM(Java虚拟机)一种用于计算设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字 ...

  7. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  8. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  9. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

随机推荐

  1. USACO 2017 February Gold

    那天打cf前无聊练手 T1.Why Did the Cow Cross the Road 题目大意:N*N的矩阵,从左上角走到右下角,走一步消耗T,每走3步消耗当前所在位置上的权值,求最小消耗 思路: ...

  2. NOIP 2014

    Prob.1 生活大爆炸版 石头剪刀布 模拟.代码: #include<cstdio> #include<cstring> #include<iostream> u ...

  3. hzoj 2301(莫比乌斯反演)

    题意 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公 数. 思路: 与先前的那个相比,这次a,c并不一定 ...

  4. Python的序列类型——List

    List 列表 List,本质是一个链表,从链表的实现角度来讲,链表的每一个结点都存放着值和指向下一个节点的指针. 因此链表在内存的存储可以是不连续的,它是一种高效的数据结构.因此列表与字符串的区别是 ...

  5. Java利用Apache POI将数据库数据导出为excel

    将数据库中的数据导出为excel文件,供其他人查看 public class POITest { public static void main(String[] args) { POITest te ...

  6. Windows笔记目录

    Windows笔记目录   Windows介绍 Windows入门--个人感想 初入Linux AD域控 FTP 服务器及权限 Server-U FTP与AD完美集成方案详解 其他 Linux大文件传 ...

  7. [Luogu 1516] 青蛙的约会

    Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事 ...

  8. Struts中的找不到StringUtils异常

    今天在Struts2框架下使用JSON插件时,程序保存找不到类org/apache/commons/lang/xwork/StringUtils,我纳闷了,commons-lang-2.5.jar和c ...

  9. Spring Cloud 服务端注册与客户端调用

    Spring Cloud 服务端注册与客户端调用 上一篇中,我们已经把Spring Cloud的服务注册中心Eureka搭建起来了,这一章,我们讲解如何将服务注册到Eureka,以及客户端如何调用服务 ...

  10. Kinect2.0 MultiSourceFrameReader 的 AcquireLatestFrame 方法获取不到帧的解决方案

    先把大致要写的东西写一下,手里的活忙完了再完善. 在代码中使用下边的语句,获取Kinect中,colorFrame, depthFrame, bodyIndex三种帧,但是经常会遇到在后边的程序中处理 ...