注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

断尘居

温柔的男人像海洋。

 
 
 
 
 

日志

 
 

VirtualCalls & InterfaceCalls  

2014-11-28 11:26:32|  分类: JVM/ HotSpot |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
VirtualCalls

When an invokevirtual call is linked, the linker resolves the call to an abstract target method. This boils down to a target class and a vtable index within that class. Since the target class is statically guaranteed by the verifier, the significant result (for execution of the call) is the vtable index.

A vtable is a display, associated with a class, of actual target methods for every virtual method which the class responds to, or any of its superclasses responds to. Just as the fields of an individual instance grow from a prefix shared with super classes toward a suffix unique to the instance's exact class, the vtable of a class grows from a prefix of method slots shared with super classes toward a suffix of methods which are unique to the class. If the class has no subclasses, those methods, though virtual, might not be overridden. But subclasses may replace those inherited prefix slots with their own overriding methods.

If a class is concrete, all of its vtable entries refer to concrete (executable) methods. If a class is abstract, some of its vtable entries may lack executable code. (They are occupied by abstract methods. If such an entry gets executed by accident, an error is thrown.)

In HotSpot, a class's vtable is allocated directly inside the InstanceKlass object which serves as metadata for the class. This means that a virtual call can proceed in two indirections: One to get the _klass field from the header of an object, and another to load the vtable slot from the InstanceKlass. (A typical offset is 200 or 300 bytes into the middle of the InstanceKlass.) After the vtable, the InstanceKlass contains other variable-sized structures, such as the itable (which is similar to the vtable) and oops maps for instance fields. This double indirection pattern is very similar to that seen in most C++ implementations. However, there is (currently) a third indirection to fetch an entry point from the Method, pointer to which occupies a vtable slot. For example, see the code in src/cpu/x86/vm/vtableStubs_x86_32.cpp.

It is legal for an invokevirtual bytecode to refer to a final method. A final method need not have a vtable slot allocated. This means that, after linking, an invokevirtual bytecode might in fact collapse into the equivalent of an invokestatic bytecode. The interpreter is prepared to do this.

The Java language exposes two states for call sites: Unlinked and linked. Once a virtual call site is linked it is handled by a vtable-based calling sequence (or a direct one, in rare cases).

Call sites in compiled code typically do not use the vtable-based calling sequence. That is because most of them are "well behaved" and can be implemented more efficiently than via a triple indirection. Even if the compiler does not take special steps to optimize a call site, there is an intermediate state, between unlinked and vtable-based, where a call site can point to a single method. This state is called "monomorphic". A monomorphic call site is one which optimistically points to the only concrete method that has ever been used at that particular call site. The calling sequence consists of verifying that the current (dynamically determined) receiver is exactly the same type as the previous call. That check is a simple pointer comparison, of a constant versus the InstanceKlass* loaded (via one indirection) from the object header. If the check succeeds, the call is made directly.

The receiver class is the one observed when the call site was first linked. If a new class is ever encountered (and this eventually happens for roughly ten percent of call sites), the call site is relinked to use the vtable based calling sequence: The address of the call instruction is patched to a vtable dispatch stub. This relinking is invisible to the Java programmer.

More details: Vtable stubs are assembled on the fly, but there are only about a hundred of them. There is one for each distinct vtable slot (offset within the InstanceKlass of the second indirection). For a monomorphic call site, the caller loads the expected class (as a InstanceKlass*) into a standard (non-argument) register, and optimistically jumps to the expected target method. All such target methods are compiled with a special "verified entry point" (VEP) which includes an instruction to compare the incoming InstanceKlass* against the receiver's _klass field. If the check succeeds, control falls through to the normal "unverified entry point" (UEP) of the compiled method. If the check fails, control branches out of the (wrong) method into a stub which performs an up-call to the JVM, to figure out what's wrong, and (usually) relink the call site.

Sample Code

Since most call sites are monomorphic, they can be completed in a single branch with a parallel receiver type check. Here is a generic instruction trace of this simple case:

monomorphic call to a virtual or interface method
callSite:
    set #expectedKlass, CHECK
    call #expectedMethod.VEP
---
expectedMethod.VEP:
    cmp (RCVR + #klass), CHECK
    jump,ne wrongMethod
compiledEntry:
    ...

Any method that can be a target of such a call has two entry points, the VEP ("verified entry point") and the UEP ("unverified entry point"). The former does a two-instruction type check and then falls into the latter.

Here is a generic instruction trace of a polymorphic interface call. It is the equivalent of the C++ virtual function dispatch.

polymorphic call to a virtual method
callSite:
    set #garbage, CHECK
    call #vtableStub[vtableSlot]
---
vtableStub[vtableSlot]:
    load (RCVR + #klass), TEM
    load (TEM + #(vtable + vtableSlot)), METHOD
    load (METHOD + #compiledEntry), TEM
    jump TEM
---
compiledEntry:
    ...

In all, that is 3 memory references and two nonlocal jumps.

Note that the intermediate "vtable stub" is customized to the vtable offset (which is usually in the 200-300 range, given that the header of a InstanceKlass is around 200 bytes, or somewhat more on LP64). The vtable stub is not customized to the klass.

The final memory reference in this instruction trade could be removed by storing a pointer to each method's UEP in vtable (and itable) entries, as C++ does. This has not been done because it would then be difficult for the interpreter to use the vtables. (An earlier version of the system had two-word vtables, to display both kinds two entry points on an equal footing, but this led to complexities and, worst of all, race conditions.) It is an interesting problem to try to invert the preference the current design gives to the interpreter; the interpreted entry point would have to be hidden somewhere at a fixed offset from the method's compiled entry point, but not close enough to disturb the tuning of the VEP.

Call sites of this polymorphic form are rarely generated directly by the JIT; instead, they are generated to call to a bootstrapping linkage routine, which then sets them (if all goes well) to the monomorphic state described above. When the second receiver type is encountered, the linkage code (at thewrongMethod jump target above) is called to transition the call site to its polymorphic state. The state changes are summarized in the leading comments of src/share/vm/code/compiledIC.hpp.



InterfaceCalls

When an invokeinterface call is linked, the linker resolves the call to an abstract target method, in an interface. This boils down to a target interface and a so-called itable index within that interface.

Target interfaces are never statically guaranteed by the JVM verifier; every invokeinterface receiver is typed as a simple object reference. Therefore (unlike invokevirtual calls), no assumptions can be made about the receiver's vtable layout. Instead, the receiver's class (as represented by its _klass field) must be checked more carefully. Where a virtual call can blindly perform two or three indirections to reach the target method, an interface call must first inspect the receiver's class to determine (a) if that class actually implements the interface, and (b) if so, where that interface's methods are recorded within that particular class.

There is no simple prefixing scheme in which an interface's methods are displayed at fixed offsets within every class that implements that interface. Instead, in the general (non-monomorphic) case, an assembly-coded stub routine must fetch a list of implemented interfaces from the receiver's InstanceKlass, and walk that list seeking the current target interface.

Once that interface is found (within the receiver's InstanceKlass), things get a little easier, because the interface's methods are arranged in an itable, or "interface method table", a display of methods whose slot structure is the same for every class that implements the interface in question. Therefore, once the interface is found within the receiver's InstanceKlass, an associated offset directs the assembly stub to an itable embedded in the InstanceKlass (just after the vtable, as one might expect). At that point, invocation proceeds as with virtual method calls.

In the interpreter, the state of a linked invokeinterface instruction consists of two words (both in the constant pool cache): The interface being sought, and the index within that interface's itable of the method being called. (There is no need to search for symbolic method names as in message-oriented languages; only the interface needs to be searched for.) In some very rare corner cases (as with invokevirtual), the linkage state of an invokeinterface instruction might actually direct the interpreter to treat the call equivalently to an invokespecial or invokevirtual. That latter case can occur when an interface declares an Object method like hashCode as an interface method; the JVM does not allocate itable slots for those methods.

Nearly the same optimizations apply to interface calls as to virtual calls. As with virtual calls, most interface calls are monomorphic, and can therefore be rendered as direct calls with a cheap check.

It is a curious fact that the searching process described above, while currently a linear search over an array embedded in the receiver's InstanceKlass, could also be implemented, with roughly equivalent performance, as a pointer-chasing search over a linked-list representation of implemented interfaces. Indeed, some languages use pointer chasing for dynamic lookup. The advantage of such a representation is a looser coupling between a class and its methods (or in JVM terms, its itables; an itable may contain either single methods or groups of methods). The looser coupling would allow a class to extend its implemented interfaces in a type-safe and compatible manner, just as a SmallTalk class can add new methods with a modest amount of pointer swapping.

Sample Code

Since most call sites are monomorphic, they complete very quickly; see the monomorphic sample code under VirtualCalls.

Here is a generic instruction trace of a polymorphic interface call.

polymorphic call to an interface method
callSite:
    set #calledInterface, CHECK
    call #itableStub[itableSlot]
---
itableStub[itableSlot]:
    load (RCVR + #klass), KLASS_TEM
    load (KLASS_TEM + #vtableSize), TEM
    add  (KLASS_TEM + TEM), SCAN_TEM
tryAgain:
        # this part is repeated zero or more times, usually zero
	load (SCAN_TEM + #itableEntry.interface), TEM
	cmp TEM, CHECK
	jump,eq foundInterface
	test TEM
	jump,z noSuchInterface
	inc #sizeof(itableEntry), SCAN_TEM
	jump tryAgain
tryAgain:
    load (SCAN_TEM + #itableEntry.interface), TEM
    cmp TEM, CHECK
    jump,eq foundInterface
foundInterface:
    load (SCAN_TEM + #itableEntry.offset), TEM
    load (KLASS_TEM + TEM + #itableSlot), METHOD
    load (METHOD + #compiledEntry), TEM
    jump TEM
---
compiledEntry:
    ...

In all, that is six memory references and two nonlocal jumps.

Note that the intermediate "itable stub" is customized to the itable offset (which is always small and often zero). It is not customized to the interface; the call site uses the CHECK register (also used by monomorphic calls) to transmit the desired interface.

For some other approaches to reducing the cost of this operation, see Faster Interface Invocation in the Da Vinci Machine Project pages.

(The alert reader will note that updating the call site from its monomorphic state to its polymorphic state requires a two-word MP-safe transaction. This is simulated by "bungee patching", in which the target of the call is temporarily patched to a safely initialized copy of the desired call site. This extra indirection is later retracted at a safe moment, by the routine ICStub::finalize.)


======================================
original:
https://wiki.openjdk.java.net/display/HotSpot/VirtualCalls
https://wiki.openjdk.java.net/display/HotSpot/InterfaceCalls
  评论这张
 
阅读(596)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017