当通过invokevirtual调用一个方法时,JVM如何知道有多少个值要弹出到一个新的帧中?

wnavrhmk  于 2022-11-07  发布在  其他
关注(0)|答案(2)|浏览(114)

当通过invokevirtual调用一个方法时,调用方法会弹出要传递给被调用方法的值沿着objectref,并将它们放置在新的堆栈帧中。
它是如何知道哪个堆栈条目是objectref的呢?我猜它是通过查看被调用方法的类型并解析它来确定要弹出多少个值来做到这一点的,但这似乎效率极低。是否有其他一些我忽略的机制?

uqjltbpv

uqjltbpv1#

当您使用类别档案格式做为起点时,方法描述项是唯一的方法,可以判断算子堆栈中的哪些值必须成为新堆栈框架的第一个局部变量。
作为规则的例外,invokeinterface指令有一个嵌入的 count,可用于确定要使用的(类型1)元素的数量。

  • invokefinterface * 指令的 count 操作数记录参数值数量的度量,其中类型为 longdouble 的参数值为 count 值提供两个单位,任何其它类型的参数提供一个单位。此信息也可以从所选方法的描述符中导出。冗余是历史性的。

这种历史冗余并没有改变JVM必须科普方法描述符作为信息源的事实,例如invokevirtualinvokestaticinvokespecialinvokedynamic。此外,要求一致的JVM * 验证 * 该信息,如果invokeinterface的计数与从方法描述符导出的计数不同,则抛出错误。
通常,验证器负责检测方法调用何时与堆栈框架的状态不一致,因此,必须处理方法描述符并模拟它们对操作数堆栈的影响。这意味着,除非您使用的JVM在实际执行每条指令之前对其进行验证,它必须处理这些描述符,即使不执行实际的调用。显而易见的解决方案是在第一步中将方法描述符转换为更易于处理的内部表示。
简而言之,这些方法描述符 * 是 * 低效的,但是通过合理的JVM实现,您只需支付一次成本,而不是每次调用。

8mmmxcuj

8mmmxcuj2#

没有一种“正确”的方法可以做到这一点,但最简单的策略是将值留在堆栈上,而被调用的方法通过负偏移量引用它们。例如,如果被调用的方法有3个参数,则它们是从基堆栈偏移量减去3、2和1。每个参数都被复制到一个局部变量中,然后以通常的方式引用。可以更新堆栈偏移量以反映参数已被使用。当然,每个局部param也可以最初由一串POP分配,每个param一个POP。
可以使用其他技巧来加快速度。局部变量的存储方式不必与堆栈不同。它们可以存储在堆栈本身上。传入的参数占据它们在堆栈上的原始位置,然后通过更新堆栈偏移量为剩余的局部变量分配额外的空间。基础堆栈偏移量被记住,并且所有局部变量都通过基本偏移量引用。
从本质上讲,局部变量就像一个堆栈槽,只是它可以在任何时候被访问,而不管当前被推到顶部的是什么。

相关问题