- 发布日期:2023-10-30 09:16 点击次数:68
[[393190]]澳门巴黎人现金网
环球好,我是阿谁永久 18 岁的老妖魔~嘘
从《JVM 内存区域永诀》这篇著述中,环球应该 get 到了,Java 造谣机内存区域不错永诀为门径计数器、Java 造谣机栈、土产货花式栈和堆。今天,咱们来围绕其中的一个区域——Java 造谣机栈,长远地张开下。
先诠释一下哈。这篇著述的标题里带了一个“携程口试官”,有标题党的嫌疑。但有一说一,确乎有读者在上一篇著述里留言说,携程口试官问他了 Java 造谣机内存方面的学问点,是以今天的标题我就“借题发达”了。
从“再会恨晚”这个词中,我估摸着这名读者在这说念口试题前边折戟千里沙了。这样说吧,口试官确乎心爱问 Java 造谣机方面的学问点,因为很能考验出别称应聘者的简直功底,是以我盘算推算多写几篇这方面的著述,但愿能给环球多少量点匡助~
Java 造谣机以花式看成基本的膨胀单元,“栈帧(Stack Frame)”则是用于补助 Java 造谣机进行花式调用和花式膨胀的基本数据结构。每一个栈帧中齐包含了局部变量表、操作数栈、动态贯串、花式复返地址和一些特殊的附加信息(比如与调试、性高手机关连的信息)。之前的著述里有提到过这些见识,并作念了一些简便扼要的先容,但我合计还不够详备,是以这篇重心要来先容一下栈帧中的这些见识。
1)局部变量表局部变量表(Local Variables Table)用来保存花式中的局部变量,以及花式参数。当 Java 源代码文献被编译成 class 文献的技巧,局部变量表的最大容量就照旧详情了。
皇冠客服飞机:@seo3687咱们来看这样一段代码。
现在已经成为明星,粉丝团越来越大。public 澳门巴黎人现金网class LocalVaraiablesTable { private void write(int age) { String name = "千里默王二"; } }
write() 花式有一个参数 age,一个局部变量 name。
然后用 Intellij IDEA 的 jclasslib 稽察一下编译后的字节码文献 LocalVaraiablesTable.class。不错看到 write() 花式的 Code 属性中,Maximum local variables(局部变量表的最大容量)的值为 3。
按理说,局部变量表的最大容量应该为 2 才对,一个 age,一个 name,为什么是 3 呢?
欧博最新网址当一个成员花式(非静态花式)被调用时,第 0 个变量其实是调用这个成员花式的对象援用,也即是阿谁大名鼎鼎的 this。调用花式 write(18),执行上是调用 write(this, 18)。
点开 Code 属性,稽察 LocalVaraiableTable 就不错看到详备的信息了。
第 0 个是 this,类型为 LocalVaraiablesTable 对象;第 1 个是花式参数 age,类型为整形 int;第 2 个是花式里面的局部变量 name,类型为字符串 String。
诚然了,局部变量表的大小并不是花式中统统局部变量的数目之和,它与变量的类型和变量的作用域辩论。当一个局部变量的作用域领域了,它占用的局部变量表中的位置就被接下来的局部变量取代了。
来看底下这段代码。
public static void method() { // ① if (true) { // ② String name = "千里默王二"; } // ③ if(true) { // ④ int age = 18; } // ⑤ }method() 花式的局部变量表大小为 1,因为是静态花式,是以不需要添加 this 看成局部变量表的第一个元素; ②的技巧局部变量有一个 name,局部变量表的大小变为 1; ③的技巧 name 变量的作用域领域; ④的技巧局部变量有一个 age,局部变量表的大小为 1; ⑤的技巧局 age 变量的作用域领域;
对于局部变量的作用域,《Effective Java》 中的第 57 条冷落:
皇冠hg86a
将局部变量的作用域最小化,不错增强代码的可读性和可珍藏性,并裁汰出错的可能性。
在此,我还有少量要辅导环球。为了尽可能检朴栈帧耗用的内存空间,局部变量表中的槽是不错重用的,就像 method() 花式演示的那样,这就意味着,合理的作用域有助于擢升门径的性能。
局部变量表的容量以槽(slot)为最小单元,一个槽不错容纳一个 32 位的数据类型(比如说 int,诚然了,《Java 造谣机门径》中莫得明确指出一个槽应该占用的内存空间大小,但我认为这样更容易清爽),像 float 和 double 这种明确占用 64 位的数据类型会占用两个紧挨着的槽。
来看底下的代码。
public void solt() { double d = 1.0; int i = 1; }
用 jclasslib 不错稽察到,solt() 花式的 Maximum local variables 的值为 4。
为什么等于 4 呢?带上 this 也就 3 个呀?
亚新现金网稽察 LocalVaraiableTable 就显然了,变量 i 的下标为 3,也就意味着变量 d 占了两个槽。
[扫码下载app,中过数字彩1千万以上的专家都在这儿!]
2)操作数栈
同局部变量表雷同,操作数栈(Operand Stack)的最大深度也在编译的技巧就详情了,被写入到了 Code 属性的 maximum stack size 中。当一个花式刚出手膨胀的技巧,操作数栈是空的,在花式膨胀经由中,会有各式字节码指示往操作数栈中写入和取出数据,也即是入栈和出栈操作。
来看底下这段代码。
public class OperandStack { public void test() { add(1,2); } private int add(int a, int b) { return a + b; } }
OperandStack 类共有 2 个花式,test() 花式中调用了 add() 花式,皇冠赔率传递了 2 个参数。用 jclasslib 不错看到,test() 花式的 maximum stack size 的值为 3。

这是因为调用成员花式的技巧会将 this 和统统参数压入栈中,调用罢了后 this 和参数齐会逐一出栈。通过 「Bytecode」 面板不错稽察到对应的字节码指示。
aload_0 用于将局部变量表中下标为 0 的援用类型的变量,也即是 this 加载到操作数栈中;
iconst_1 用于将整数 1 加载到操作数栈中; iconst_2 用于将整数 2 加载到操作数栈中; invokevirtual 用于调用对象的成员花式; pop 用于将栈顶的值出栈; return 为 void 花式的复返指示。再来看一下 add() 花式的字节码指示。
操作数中的数据类型必须与字节码指示匹配,以上头的 iadd 指示为例,该指示只可用于整形数据的加法运算,它在膨胀的技巧,栈顶的两个数据必须是 int 类型的,不成出现一个 long 型和一个 double 型的数据进行 iadd 号召相加的情况。
3)动态贯串
每个栈帧齐包含了一个指向运行频繁量池中该栈帧所属花式的援用,握有这个援用是为了补助花式调用经由中的动态贯串(Dynamic Linking)。
来看底下这段代码。
public class DynamicLinking { static abstract class Human { protected abstract void sayHello(); } static class Man extends Human { @Override protected void sayHello() { System.out.println("男东说念主哭吧哭吧不是罪"); } } static class Woman extends Human { @Override protected void sayHello() { System.out.println("山下的女东说念主是老虎"); } } public static void main(String[] args) { Human man = new Man(); Human woman = new Woman(); man.sayHello(); woman.sayHello(); man = new Woman(); man.sayHello(); } }
环球对 Java 重写有了解的话,应该能看懂这段代码的兴致。Man 类和 Woman 类秉承了 Human 类,况且重写了 sayHello() 花式。来看一下运行成果:
男东说念主哭吧哭吧不是罪 山下的女东说念主是老虎 山下的女东说念主是老虎
这个运行成果很好清爽,man 的援用类型为 Human,但指向的是 Man 对象,woman 的援用类型也为 Human,但指向的是 Woman 对象;之后,man 又指向了新的 Woman 对象。
从面向对象编程的角度,从多态的角度,咱们对运行成果是很好清爽的,但站在 Java 造谣机的角度,它是如何判断 man 和 woman 该调用哪个花式的呢?
用 jclasslib 看一下 main 花式的字节码指示。
良好,从字节码的角度来看,man.sayHello()(第 10 行)和 woman.sayHello()(第 12 行)的字节码是十足调换的,但咱们齐知说念,这两句指示最终膨胀的目的花式并不调换。
究竟发生了什么呢?
还得从 invokevirtual 这个指示入辖下手,看它是如何达成多态的。凭据《Java 造谣机门径》,invokevirtual 指示在运行时的领略经由不错分为以下几步:
①、找到操作数栈顶的元素所指向的对象的执行类型,记作 C。
蒙地卡罗赌场②、要是在类型 C 中找到与常量池中的姿色符匹配的花式,则进行探望权限校验,要是通过则复返这个花式的成功援用,查找领域;不然复返 java.lang.IllegalAccessError 特殊。
③、不然,按照秉承关系从下往上一次对 C 的各个父类进行第二步的搜索和考证。
bet365体育平台④、要是恒久莫得找到稳健的花式,则抛出 java.lang.AbstractMethodError 特殊。
也即是说,invokevirtual 指示在第一步的技巧就详情了运行时的执行类型,是以两次调用中的 invokevirtual 指示并不是把常量池中花式的标识援用领略到成功援用上就领域了,还会凭据花式收受者的执行类型来选择花式版块,这个经由即是 Java 重写的骨子。咱们把这种在运行期凭据执行类型详情花式膨胀版块的经由称为动态贯串。
4)花式复返地址
当一个花式出手膨胀后,只须两种样式不错退出这个花式:
浩荡退出,可能会有复返值传递给表层的花式调用者,花式是否有复返值以及复返值的类型凭据花式复返的指示来决定,像之前提到的 ireturn 用于复返 int 类型,return 用于 void 花式;还有其他的一些,lreturn 用于 long 型,freturn 用于 float,dreturn 用于 double,areturn 用于援用类型。
特殊退出,花式在膨胀的经由中际遇了特殊,况且莫得取得妥善的处分,这种情况下,是不会给它的表层调用者复返任何值的。
皇冠信用盘代理无论是哪种样式退出,在花式退出后,齐必须复返到花式首先被调用时的位置,门径身手继续膨胀。一般来说,花式浩荡退出的技巧,PC 计数器的值会看成复返地址,栈帧中很可能会保存这个计数器的值,特殊退出时则不会。
花式退出的经由执行上等同于把面前栈帧出栈,因此接下来可能膨胀的操作有:收复表层花式的局部变量表和操作数栈,把复返值(要是有的话)压入调用者栈帧的操作数栈中,调理 PC 计数器的值,找到下一条要膨胀的指示等。
本文转载自微信公众号「千里默王二」,不错通过以下二维码柔软。转载本文请辩论千里默王二公众号。