Hi Benjamin,

I've been thinking about this, and I believe I know what the issue might be. 
Unfortunately, I don't currently have a good solution for it (although I'll be 
thinking some more about it).

Basically, call sites are linked with method handles that are guarded with a 
test for exact receiver type (basically obj.getClass() == X.class). Call sites 
further can have up to 8 methods linked into them (in a LRU fashion) in a 
waterfall cascade of guard-with-tests. If your call site sees more than 8 
receiver types (this number is fixed right now), it'll keep relinking as it'll 
only remember the most recent 8.

Even if you don't override the method in subclasses, we can't use a more 
generic guard because we can't prove that there won't ever be a new subclass 
that won't overload the method. Note I said overload, not override: that's not 
a mistake. Here's a scenario:

public class A { 
    public void foo(Object o) { ... }
}

public class B extends A {
}

Now imagine a script call site "a.foo('Hello')". When it's hit with an instance 
of B, we'll use "a.getClass() == B.class" as the guard. Now, if you have a 
bunch of subclasses B1…B12 all extending A, you'll end up with 12 linkages to 
the same method, but all guarded with a different "a.getClass() == Bn.class" 
guard. Actually, as I said above, you'll end up with a call site incorporating 
the 8 most recently used ones, and force relinking when the 9th comes along. 

You could ask "what'd be the harm in linking to "A.foo(Object)" method just 
once with "a instanceof A" guard? The harm becomes apparent if we now define

public class B extends A {
    public void foo(String s) { ... }
}

With instanceof linkage, invocation at the call site with an instance of C 
would pass the guard, and invoke A.foo(Object), which is incorrect as it'd be 
expected to invoke C.foo(String) instead. As you can see, this is not a matter 
of a subclass overriding foo(Object), but rather it's a matter of the subclass 
overloading the "foo" name with a new signature.

The only strategy we have for avoiding this is at the moment is almost always 
linking with exact receiver class guards :-(

On a sidenote, I said "almost always" above as there's a special class of 
methods we can, in fact, link with "instanceof" guards on the most generic 
declaring superclass: methods taking zero arguments (e.g. all property 
getters). Since overload choice is actually per-arity, zero-argument methods 
can't effectively be overloaded, so for them we actually use "instanceof" 
guards. But, sadly, we can't use them for any other methods.

One strategy to cope with the issue would be to check during linking if none of 
the currently known subclasses add new overloads to the method (or even, not 
overload it in a manner where a different method would be chosen for the static 
type at the call site), and if they don't, then link with a switch point 
representing this invariant. Then, whenever a subclass is loaded into the VM 
that invalidates the assumption, invalidate the switch points. Unfortunately, 
this strategy requires whole-VM knowledge of loaded classes, and we could only 
do it if we added a java.lang.instrument agent as a mandatory component in 
Nashorn. 

(Another sidenote: we're trying very hard to keep Nashorn from relying on any 
implementation-specific or undocumented platform or VM features; so far we have 
always managed to rely solely on public Java APIs also because we'd like to 
prove that they're sufficient for a dynamic language implementation on the JVM.)

Alternatively, we could also try to prove a weaker assumption that the chosen 
method would always be the one invoked at the call site (e.g. the method type 
of the call site guarantees that there can't ever be a more specific method to 
invoke), but in reality this'd be quite hard and since Nashorn internally 
mostly only uses boolean, int, long, double, and Object as the call site 
signatures, it probably also wouldn't be effective (e.g. we could nearly never 
prove this invariant). So that's probably not worth it.

As a yet another solution, we might give you a system property or other 
configuration means of allowing link chains longer than 8 (in your case, if you 
have 12 subclasses, then 12 should be enough).

Sorry for not having a better answer…
  Attila.

On Nov 19, 2014, at 10:40 AM, Benjamin Sieffert <benjamin.sieff...@metrigo.de> 
wrote:

> Hello everyone,
> 
> it started with a peculiar obversion about our nashorn-utilising
> application, that I made: It continues to load around a hundred new
> anonymous classes *per second*, even without new scripts being introduced –
> i.e. we are just running the same javascripts over and over again, with
> different arguments.
> So I ran the application with -tcs=miss and from what I see, eventually
> there will be only a single call left that is producing all the output and
> therefor, I believe, all the memory load. (Am I correct in this assumption?)
> 
> What I can say about the call is the following:
> 
> - return type is an array of differing length (but always of the same type)
> - there are two arguments, of which the first one will always exactly match
> the declaration, the second one is a subclass of the one used in the
> declaration – but always the same subclass
> - method is implemented in an abstract class
> - receiver is one of about a dozen classes that inherit from this abstract
> class
> - none of the receivers overwrite the original implementation or overload
> the method
> 
> When I look into the trace output, there's often a bunch of
> "TAG MISS library:212 dyn:getMethod|getProp|getElem:<methodname> …"
> in a row, then a whole lot of
> "TAG MISS library:212
> dyn:call([jdk.internal.dynalink.beans.SimpleDynamicMethod …"
> with a bit of the first one inbetween.
> 
> Is this a known issue? Is there something I can do to alleviate the
> problem? As it is, I might just end up implementing the whole chunk in Java
> and be done with it, but I thought this might be worthy of some discussion.
> If there's some important information that I have left out, I'll be glad to
> follow up with it.
> 
> Regards,
> Benjamin
> 
> -- 
> Benjamin Sieffert
> metrigo GmbH
> Sternstr. 106
> 20357 Hamburg
> 
> Geschäftsführer: Christian Müller, Tobias Schlottke, Philipp Westermeyer,
> Martin Rieß
> Die Gesellschaft ist eingetragen beim Registergericht Hamburg
> Nr. HRB 120447.

Reply via email to