The magic of self-patching vtable
Ioi Lam
ioi.lam at oracle.com
Fri Dec 14 14:04:18 PST 2012
Moving this discussion to hotspot-dev per John's suggestion.
Actually, strictly from the point of view of CDS, it would be OK for
objects in the RO region to have vtables. This is because the _vptr is
fixed during dump time, and never modified during runtime.
Only the contents of the vtables (pointed to by _vptr) are updated at
CDS image load time.
- Ioi
On 12/14/2012 01:31 PM, John Rose wrote:
> This is really interesting.
>
> I'll bet there could be a shell script that could detect and flag unintentional vtables on RO classes, at least on Linux or Solaris. That would be enough to block JPRT submissions.
>
> -- John
>
> On Dec 14, 2012, at 12:56 PM, Coleen Phillimore <coleen.phillimore at oracle.com> wrote:
>
>> On 12/14/2012 3:46 PM, Ioi Lam wrote:
>>> Hi Coleen,
>>>
>>> Yes, I could find lots comments for 'how' the self-patching vtable works. I just couldn't find a justification for 'why' it needs to be done this way.
>>>
>>> The _vptr of all the Metadata objects in the CDS image are already fixed during dump time. They are never changed at run-time. I.e., immediately after the CDS image is loaded, we have this invariant:
>>>
>>> Universe::boolArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>> Universe::byteArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>> Universe::charArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>> ...
>>>
>>> where
>>>
>>> cds_TypeArrayKlass_vtable points to somewhere slightly above the "md" region of the mapped CDS image.
>>>
>>> If we can find out the size of the vtable for TypeArrayKlass, using the method I proposed below, why can't we simply do this as part of CDS image loading?
>>>
>>> {
>>> TypeArrayKlass o;
>>> void * real_vptr = *(void**)(&o);// == o._vptr;
>>> memcpy(cds_TypeArrayKlass_vtable, real_vptr, sizeof(void*) * num_vtable_slots_TypeArrayKlass);
>>> }
>> Yeah, that would be good except I can't figure out how that works, but I'll take your word for it rather than trying to work it out right now.
>>
>> You'd do this restoration in the function restore_unshareable_info() instead of loading (lazily) though.
>>
>> Coleen
>>
>>> Thanks
>>> - Ioi
>>>
>>> On 12/14/2012 05:03 AM, Coleen Phillimore wrote:
>>>> Hi Ioi,
>>>>
>>>> There are comments about self-patching vtables in memory/universe.cpp and memory/metaspaceShared.cpp and the target dependent metaspaceShared_<cpu>.cpp code. You have summarized how/why it works quite nicely. The main reason for vtable patching is that the .text is in different places for the executables which load the shared archive, so we have this code to fix it up in the miscellaneous code section.
>>>>
>>>> The other thing you might have left off which people should be very aware of is that metadata that is shared read-only, like classes ConstMethod, Array<alltypes>, and Symbols. Adding virtual function calls to these will result in them having a self-patching vtable and they will no longer be read only. Do not add virtual functions to these types! I hope there are enough comments warning of this. I cannot think of a programmatic way to disallow this.
>>>>
>>>> I have another comment for your [5]-[6] below. The cpu dependent code is to create the self patching entries per platform. We can't tell how long the vtable is so picked 200 for historical purposes. But we still need the cpu dependent code because we still need vtable patching because of point [2] and it's done with macro assembler. If the macro assembler was made to be a high level macro assembler, we wouldn't need cpu dependent code. Knowing in advance the size of the vtable would save the hard-coded constant, but that's not really a big benefit.
>>>>
>>>> An aside why 200 was picked. In the pre-permgen world, the metadata in the shared space were Java objects and were inherited from oopDesc. Any new virtual function added for GC in oopDesc would regularly blow out the length of the vtable size. This isn't the case anymore since the base class for these types is Metadata and there aren't very many virtual functions there. The type Klass* has a lot more, but still way under 200. A sanity check in debug mode might be useful.
>>>>
>>>> thanks,
>>>> Coleen
>>>>
>>>> On 12/14/2012 1:17 AM, Ioi Lam wrote:
>>>>> Hi,
>>>>>
>>>>> I am reading the CDS code and came upon the rather intriguing concept of "self patching vtables" -- i.e., patch_klass_vtables() and friends in hotspot/src/share/vm/memory/metaspaceShared.cpp.
>>>>>
>>>>> I couldn't find any comments in the source code for the reasons for such a complicated mechanism. Here's my understanding of it. Please let me know if this is correct:
>>>>>
>>>>> [1] Objects of the Metadata types (such as Klass and ConstantPool) have vtables.
>>>>> In GCC this is the field <Type>::_vptr, i.e., first word in
>>>>> the object.
>>>>>
>>>>> [2] Addresses of the vtables and the methods may be different across JVM runs,
>>>>> if libjvm.so is loaded at a different base address.
>>>>>
>>>>> [3] Although all of the Metadata objects are mapped R/W in the CDS image,
>>>>> at load time, we don't want to rewrite _vptr of each Metadata object
>>>>> (to maximize sharing).
>>>>>
>>>>> [4] Therefore, we redirect _vptr to our own vtables at CDS image dump time.
>>>>> Then,we patch our own vtables at run time.
>>>>>
>>>>> [5] The problem with [4] is with most C++ compilers (all?), there is no
>>>>> easy way to tell the size of the vtable of a given type.
>>>>>
>>>>> [6] We cannot safely copy more than the size of the real vtable, because the
>>>>> real vtable may be at the end of the code section; reading past its end
>>>>> would cause the VM to crash.
>>>>>
>>>>> As a result, the current design of the 'self patching vtable' is to
>>>>> create a vtable that's "big enough" (currently with 200 method slots).
>>>>> Each slot points to a generated stub that knows the C++ type and
>>>>> virtual method index of the invoked method. When the stub is invoked,
>>>>> it will look up the real method and patch the vtable accordingly.
>>>>>
>>>>> -----
>>>>>
>>>>> If I am right that the whole reason for the self patching vtables is getting the length of the vtable, wouldn't it be much easier if we do:
>>>>>
>>>>> class InstanceKlassVTableLengthFinder: public InstanceKlass {
>>>>> public:
>>>>> virtual void ___end_of_vtable_marker();
>>>>> };
>>>>>
>>>>> and then just search for ___end_of_vtable_marker() from the _vptr?
>>>>>
>>>>> That way we can get rid of all the CPU dependent code related to self-patching vtables.
>>>>>
>>>>> - Ioi
More information about the hotspot-dev
mailing list