当通过invokevirtual调用一个方法时,调用方法会弹出要传递给被调用方法的值沿着objectref,并将它们放置在新的堆栈帧中。它是如何知道哪个堆栈条目是objectref的呢?我猜它是通过查看被调用方法的类型并解析它来确定要弹出多少个值来做到这一点的,但这似乎效率极低。是否有其他一些我忽略的机制?
uqjltbpv1#
当您使用类别档案格式做为起点时,方法描述项是唯一的方法,可以判断算子堆栈中的哪些值必须成为新堆栈框架的第一个局部变量。作为规则的例外,invokeinterface指令有一个嵌入的 count,可用于确定要使用的(类型1)元素的数量。
invokeinterface
这种历史冗余并没有改变JVM必须科普方法描述符作为信息源的事实,例如invokevirtual、invokestatic、invokespecial或invokedynamic。此外,要求一致的JVM * 验证 * 该信息,如果invokeinterface的计数与从方法描述符导出的计数不同,则抛出错误。通常,验证器负责检测方法调用何时与堆栈框架的状态不一致,因此,必须处理方法描述符并模拟它们对操作数堆栈的影响。这意味着,除非您使用的JVM在实际执行每条指令之前对其进行验证,它必须处理这些描述符,即使不执行实际的调用。显而易见的解决方案是在第一步中将方法描述符转换为更易于处理的内部表示。简而言之,这些方法描述符 * 是 * 低效的,但是通过合理的JVM实现,您只需支付一次成本,而不是每次调用。
invokevirtual
invokestatic
invokespecial
invokedynamic
8mmmxcuj2#
没有一种“正确”的方法可以做到这一点,但最简单的策略是将值留在堆栈上,而被调用的方法通过负偏移量引用它们。例如,如果被调用的方法有3个参数,则它们是从基堆栈偏移量减去3、2和1。每个参数都被复制到一个局部变量中,然后以通常的方式引用。可以更新堆栈偏移量以反映参数已被使用。当然,每个局部param也可以最初由一串POP分配,每个param一个POP。可以使用其他技巧来加快速度。局部变量的存储方式不必与堆栈不同。它们可以存储在堆栈本身上。传入的参数占据它们在堆栈上的原始位置,然后通过更新堆栈偏移量为剩余的局部变量分配额外的空间。基础堆栈偏移量被记住,并且所有局部变量都通过基本偏移量引用。从本质上讲,局部变量就像一个堆栈槽,只是它可以在任何时候被访问,而不管当前被推到顶部的是什么。
2条答案
按热度按时间uqjltbpv1#
当您使用类别档案格式做为起点时,方法描述项是唯一的方法,可以判断算子堆栈中的哪些值必须成为新堆栈框架的第一个局部变量。
作为规则的例外,
invokeinterface
指令有一个嵌入的 count,可用于确定要使用的(类型1)元素的数量。这种历史冗余并没有改变JVM必须科普方法描述符作为信息源的事实,例如
invokevirtual
、invokestatic
、invokespecial
或invokedynamic
。此外,要求一致的JVM * 验证 * 该信息,如果invokeinterface
的计数与从方法描述符导出的计数不同,则抛出错误。通常,验证器负责检测方法调用何时与堆栈框架的状态不一致,因此,必须处理方法描述符并模拟它们对操作数堆栈的影响。这意味着,除非您使用的JVM在实际执行每条指令之前对其进行验证,它必须处理这些描述符,即使不执行实际的调用。显而易见的解决方案是在第一步中将方法描述符转换为更易于处理的内部表示。
简而言之,这些方法描述符 * 是 * 低效的,但是通过合理的JVM实现,您只需支付一次成本,而不是每次调用。
8mmmxcuj2#
没有一种“正确”的方法可以做到这一点,但最简单的策略是将值留在堆栈上,而被调用的方法通过负偏移量引用它们。例如,如果被调用的方法有3个参数,则它们是从基堆栈偏移量减去3、2和1。每个参数都被复制到一个局部变量中,然后以通常的方式引用。可以更新堆栈偏移量以反映参数已被使用。当然,每个局部param也可以最初由一串POP分配,每个param一个POP。
可以使用其他技巧来加快速度。局部变量的存储方式不必与堆栈不同。它们可以存储在堆栈本身上。传入的参数占据它们在堆栈上的原始位置,然后通过更新堆栈偏移量为剩余的局部变量分配额外的空间。基础堆栈偏移量被记住,并且所有局部变量都通过基本偏移量引用。
从本质上讲,局部变量就像一个堆栈槽,只是它可以在任何时候被访问,而不管当前被推到顶部的是什么。