java局部变量表最大值_局部变量表(Local Variable Table)

本文详细介绍了Java方法的局部变量表,包括局部变量的数量、Slot插槽以及插槽复用机制。讨论了非静态方法的this参数、代码块内的局部变量、异常处理中的局部变量,并分析了Slot如何存储不同数据类型。还通过实例探讨了局部变量表的Slot复用如何影响垃圾回收行为。
摘要由CSDN通过智能技术生成

Java中方法的局部变量表(Local Variable Table)是一组变量的存储空间,用于存放方法的参数和方法内定义的局部变量,在Class编译的时候,Code属性中的 max_local值表示了该方法的最大局部变量表的长度。

1、局部变量的数量

1.1 实例方法的局部变量数量

为了方便演示,这里写了一个非常普通的Bean,代码如下

package com.zhoutao.bytefile;public class ExampleClass {private int a = 3;public int getA() {return a;}public void setA(int a) {this.a = a;}}

通过字节码工具 jclasslib-bytecode-viewer,可以非常清楚的看到总共有两个局部变量,this和a其中this的类型正是当前所在类的类型Lcom.zhoutao.bytefile.ExampleClass,a的类型为I,表示原生的int类型。

setter方法,其参数只有1个,为什么局部变量表的会有两个变量呢?

79f8475bc644a6db992715f2b4743da5.png

这是因为对于非静态方法而言,方法的第一个参数总是this,指向当前对象,这种将this作为参数传递给方法的方式,和Python的方法是第一个参数为self类似,只不过前者为隐式传递,后者为显示传递。

1.2 代码块中定义的局部变量

修改getA()方法的代码,调整为如下的,按照预想,应该有三个局部便令,this,s1以及s2。但正确的结果是有两个,this 和 s1, s2 并不是该方法的局部变量。

同样类似的还有在try{}语句块中定义的变量以及在finally定义的变量。

public int getA() {int s1 = 1;{int s2 = 1;}return a;}

1.3 异常捕获中的局部变量

继续修改getA() 方法的代码,添加一些异常处理的方式

public int getA() {try {} catch (RuntimeException e) {} catch (Exception e) {}return a;}

此时,getA() 方法的局部变量的数量为1个,而非3个,这是因为JVM会自动优化一些不必要的变量,所以e变量没有使用到,并不会作为局部变量出现在局部变量表中。

4da0de9240939455247510d2ce2b9524.png

在catch的语句中添加对变量的使用,比如下面的操作

public int getA() {try {throw new ClassNotFoundException();} catch (RuntimeException e) {e.printStackTrace();} catch (ClassNotFoundException e1) {e1.printStackTrace();}return a;}

在异常处理添加了对异常的使用之后,JVM不在进行优化,所以也作为局部变量显示出来,所以这里可以观察到确实存在3个局部变量,分别是 this,e,e1

d54c00ed4dc550d5b269108adc10777f.png

但是e和e1只能存在一个,当存在e的时候e1不可能存在,所以在Code属性中,最大的局部变量为2,而非3.

53a11c68a19b035f907ca205658e9e22.png

2、Slot 插槽

LocalVariableTable 以slot的最小内存单元,虚拟机并未指定slot占用多少个字节,仅描述,每个slot应该能存放一个 boolean,byte,char,short,int,float, reference,returnAddress 等类型的数据,这8种类型的数据都可以使用32位或者更小的内存空间来存放,所以一个64位的虚拟机也可以使用64位的slot位宽来实现Slot,但是要使用对齐和补白的手段来使之看上去和32位数据无异。

7480322438f4bbc9b06741590dc8e69d.png

对于64位的数据类型,虚拟机会以高位对齐的手段来连续分配2个Slot的空间,来存放这些数据,64位的数据类型仅有float和long类型两种,这也与  'float & long类型的非原子协定'中一次把数据分割为2个32位的数据操作有些类似。

虚拟机通过索引的方式方位局部变量表,32位的系统中,索引为n就代表第n个slot,如果是64位系统,第则同时会使用n和n+1的slot两个Slot,不允许单独访问一个Slot,JVM规范中明确要求遇到这种字节码操作应该抛出异常。

同在字节码分析的章节异常,在LocalVariableTable的索引中,如果访问的是非实例方法(即类的方法)那么第0位表示的就是this 这与Python的面向对象编程非常相似。其余参数则按照顺序分配Slot,然后在根据方法表内部定义的顺序分配其余Slot,总结来说其分配顺序如下:this变量

方法中的其他形参

方法内部定义的局部变量

例如如下的代码的Slot分配如下

public class ExampleClass {public void exampleMethod(int a,int b,short c,char d){String a1= "";Object a2 = new Object();...}}

0a294a5440c4884796fac7942b644b74.png

3、Slot 插槽复用机制以及影响

为了节省栈帧的使用空间,局部变量表中的Slot是可以重用的。有的局部变量的作用域并不会完全覆盖整个方法的作用域,如果当前PC计数器的值已经超出了某个变量的作用域,那么这个变量所对应的slot就会被重复使用 。这样设备的作用是为了节省栈帧内存空间,但在一些特殊情况下会影响系统的GC行为。

如下面的代码:

public static void main(String[] args) throws InterruptedException {{byte[] placeholder = new byte[64 * 1024 * 1024];}System.gc();// 为防止GC不及时,特延时1sTimeUnit.SECONDS.sleep(1);}

运行前在执行命令添加参数:-verbose:gc

[GC (System.gc()) 69468K->66104K(251392K), 0.0013540 secs][Full GC (System.gc()) 66104K->65938K(251392K), 0.0049892 secs]

按照道理程序在运行的时候,System.gc(); 发生的时候系统将会回收placeholder所占用的内存, 但是系统并没有正确的回收掉placeholder 所占用的内存。

修改程序,在System.gc(); 调用之前添加 int a = 0;  输出执行结果如下:

[GC (System.gc()) 69468K->66120K(251392K), 0.0019919 secs][Full GC (System.gc()) 66120K->402K(251392K), 0.0045493 secs]

如上面的显示可以看成,程序正常的回收内存 66120K->402K  ,那么为什么 添加了 int a = 0; 这段无关紧要的代码就可以正常的GC 呢?

局部变量表所引用的变量占用的内存否可以引用,主要看局部变量所在的 Slot 是否仍在引用此 变量,在 System.gc(); 的时候,slot并未失去对 placeholder 的引用,所以无法正常的GC,在添加一个 int a = 0 之后,系统就可以正常回收这一段内存,这是因为在定义局部变量a之后,由于slot 复用他会将原placeholder所在的slot 复用,原内存地址不在被引用,所以可以正常的GC回收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值