[9] RFR(L) 8013267 : move MemberNameTable from native code to Java heap, use to intern MemberNames

David Chase david.r.chase at oracle.com
Tue Nov 11 18:58:30 UTC 2014

On 2014-11-08, at 10:07 AM, Peter Levart <peter.levart at gmail.com> wrote:
> Now let's take for example one of the MemberName.make() methods that return interned MemberNames:
>  206     public static MemberName make(Method m, boolean wantSpecial) {
>  207         // Unreflected member names are resolved so intern them here.
>  208         MemberName tmp0 = null;
>  209         InternTransaction tx = new InternTransaction(m.getDeclaringClass());
>  210         while (tmp0 == null) {
>  211             MemberName tmp = new MemberName(m, wantSpecial);
>  212             tmp0 = tx.tryIntern(tmp);
>  213         }
>  214         return tmp0;
>  215     }
> I'm trying to understand the workings of InternTransaction helper class (and find an example that breaks it). You create an instance of it, passing Method's declaringClass. You then (in retry loop) create a resolved MemberName from the Method and wantSpecial flag. This MemberName's clazz can apparently differ from Method's declaringClass. I don't know when and why this happens, but apparently it can (super method?), so in InternTransaction.tryIntern() you do...
>  363             if (member_name.isResolved()) {
>  364                 if (member_name.clazz != tx_class) {
>  365                     Class prev_tx_class = tx_class;
>  366                     int prev_txn_token = txn_token;
>  367                     tx_class = member_name.clazz;
>  368                     txn_token = internTxnToken(tx_class);
>  369                     // Zero is a special case.
>  370                     if (txn_token != 0 ||
>  371                         prev_txn_token != internTxnToken(prev_tx_class)) {
>  372                         // Resolved class is different and at least one
>  373                         // redef of it occurred, therefore repeat with
>  374                         // proper class for race consistency checking.
>  375                         return null;
>  376                     }
>  377                 }
>  378                 member_name = member_name.intern(txn_token);
>  379                 if (member_name == null) {
>  380                     // Update the token for the next try.
>  381                     txn_token = internTxnToken(tx_class);
>  382                 }
>  383             }
> Now let's assume that the resolved member_name.clazz differs from Method's declaringClass. Let's assume also that either member_name.clazz has had at least one redefinition or Method's declaringClass has been redefined between creating InternTransaction and reading member_name.clazz's txn_token. You return 'null' in such case, concluding that not only the resolved member_name.clazz redefinition matters, but Method's declaringClass redefinition can also invalidate resolved MemberName am I right? It would be helpful if I could understand when and how Method's declaringClass redefinition can affect member_name. Can it affect which clazz is resolved for member_name?

If a declaring class is redefined before a MemberName is “published” to the VM, then there is a risk that its secret fields will have gone stale because the referenced VM methods changed but were not updated.  Therefore, the resolution must be retried to get a fresh resolution that is known not to be stale.  There is sort of a glitch in the race-checking protocol; I don’t have certain knowledge which class will be resolved, so if I guessed wrong (and the common-case no redefinition at all check fails) then I am forced to retry and get a fresh, known-good resolution.

However, based on my understanding of what is (not) allowed in class redefinition, what differs after redefinition is only the code of the method, and not the owner — that is, if D.m resolved to B.m before redefinition of D, C, or B, then it will always resolve to B.m — but the definition of B.m itself might have changed (from the test cases, it might print “foo” instead of “bar”).  Or to put it differently, the methods change, but their hierarchy does not.

> Anyway, you return null in such case from an updated InternTransaction (tx_class and txn_token are now updated to have values for resolved member_name.clazz). In next round the checks of newly constructed and resolved member_name are not performed against Method's declaringClass but against previous round's member_name.clazz. Is this what is intended?

> I can see there has to be a stop condition for loop to end, but shouldn't checks for Method's declaringClass redefinition be performed in every iteration (in addition to the check for member_name.clazz redefinition if it differs from Method's declaringClass)?

To the best of my understanding (see restrictions above) the tx_class ought to be wrong at most once;
all subsequent resolutions including those that span a class redefinition should return the same class,
so it suffices to detect redefinition of the method itself.

I’ve incorporated your other changes (not yet the linear-scan hash table) and will be retesting.
One thing I wonder about for both hash table and binary search is if the first try should be attempted with no lock to avoid the overhead of synchronization; I expect that looking will be more common than interning, which in turn will be (vastly) more common than class redefinition.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/attachments/20141111/f3b18425/signature-0001.asc>

More information about the hotspot-compiler-dev mailing list