当通过invokevirtual调用方法时,调用方法会弹出要传递给被调用方法的值以及objectref,并将它们放置在新的堆栈框架中。
它如何知道哪个堆栈条目是objectref?我的猜测是,它是通过查看被调用方法的类型来做到这一点的,并对其进行分析以确定要弹出多少值,但这似乎效率极低。还有其他我忽略的机制吗?
发布于 2021-11-22 13:26:55
当您使用类文件格式作为起点时,方法描述符是确定这些值的唯一方法,操作数堆栈中的值必须成为新堆栈帧的第一个局部变量。
作为规则的例外,invokeinterface指令有一个嵌入式计数,可以用来确定要使用的(类型1)元素的数量。正如文献资料所说:
invokeinterface指令的计数操作数记录参数值数量的度量,其中,long或type double类型的参数值对计数值贡献两个单位,而任何其他类型的参数值贡献一个单位。该信息还可以从所选方法的描述符中导出。冗余是历史遗留下来的。
这种历史冗余不会改变JVM必须处理方法描述符作为此信息来源的事实,例如对于invokevirtual、invokestatic、invokespecial或invokedynamic。此外,如果JVM的计数与从方法描述符派生的计数不同,则将抛出一个错误。
通常,验证器负责检测方法调用是否与堆栈帧状态不一致,因此必须处理方法描述符并对其对操作数堆栈的影响进行建模。这意味着,除非您使用JVM在实际执行之前验证每条指令,否则即使不执行实际调用,它也必须处理这些描述符。显而易见的解决方案是在第一步将方法描述符转换为更容易处理的内部表示。
简而言之,这些方法描述符效率很低,但是有了合理的JVM实现,您只需要支付一次费用,而不是每次调用。
发布于 2021-11-20 06:29:07
没有一种“正确”的方法可以做到这一点,但最简单的策略是将值留在堆栈中,被调用的方法通过负偏移来引用它们。例如,如果被调用的方法有3个参数,则从基堆栈偏移量减去3、2和1引用它们。每个参数被复制到一个局部变量中,然后以通常的方式引用。可以更新堆栈偏移量以反映已使用的参数。当然,每个本地param最初也可以由一组pops分配,每个param分配一个。
还可以用其他的技巧来加快速度。没有理由需要以与堆栈不同的方式存储局部变量。它们可以存储在堆栈本身上。传入的params占据堆栈上的原始位置,然后通过更新堆栈偏移量为剩余的局部变量分配额外的空间。记住基本堆栈偏移量,并通过基偏移量引用所有局部变量。
从本质上说,局部变量就像堆栈插槽一样,只是可以在任何时候访问它,不管当前推到顶部的是什么。
https://stackoverflow.com/questions/70043595
复制相似问题