A certain (type of?) callsite seems to always require relinking
Benjamin Sieffert
benjamin.sieffert at metrigo.de
Thu Nov 20 08:47:15 UTC 2014
Hi,
well, thanks for the interesting insight into nashorn's inner workings. I
was quickly able to verify that this was indeed what was causing the
phenomenon. With exactly 8 different receiver-classes, the loaded class
count now remains stable.
The one thing I'm left wondering, though, is: Accepting the performance
impact for now, isn't there a better resource to channel it through?
What I mean is, in this stable application state, there are about 22.000
classes loaded. With more than 8 receiver classes, the count will go up to
700.000, where a full GC kicks in and collects all but (those) 22k classes,
because all the others are obviously discarded linkages of the problematic
callsite. Now this is with a CodeCache of 800M, where we only need about
100M. One can easily imagine how with more tightly tuned settings (say, a
120M cap on the CodeCache, or some low cap on the Metaspace), the frequency
of full GCs would become quite scary. Even more so when we introduce a
second problematic callsite.
And in profiling the application then, I think this would be very hard to
make out for a "naïve user" that this is, in a way, "normal behaviour".
I also seem to remember, from an earlier discussion, that there are
megamorphic callsites in nashorn. Would this not qualify as one?
- Benjamin
On 19 November 2014 15:41, Attila Szegedi <attila.szegedi at oracle.com> wrote:
> 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()
> == B*n*.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.sieffert at 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.
>
>
>
--
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.
More information about the nashorn-dev
mailing list