<div dir="ltr">Hi Vitaly,<div><br></div><div>Haha. I've actually fixed the exact same problem in Zing JVM when I found this out a while ago. Do you guys want the patch be upstreamed?</div><div><br></div><div>Here's the bug description that I wrote for Zing, but it applies to HotSpot as well (since we inherited that bug from HotSpot):</div><div><br></div><div><div>This bug is to track an enhancement that would allow compilation and inlining of "bridge constructors" for private inner classes, generated by javac.</div><div><br></div><div>In HotSpot's compilation policy, and C2's inlining heuristic, if a method/constructor is found to have unloaded classes in its signature, then there are special handling:</div><div> * in compilation policy, if a method is about to be triggered a C2 compilation, and there are unloaded classes in its signature, then these classes are forced to be loaded before compilation;</div><div> * in C2, when a method is considered to be a candidate for inlining, if there are unloaded classes in its signature, it will NOT be inlined.</div><div><br></div><div>It's questionable whether or not the C2 inlining heuristic is profitable in general, but there's a case where it's definitely not profitable - when dealing with "bridge constructors" generated by javac.</div><div><br></div><div>When javac sees a private inner class with no explicit constructors, e.g.</div><div><br></div><div>> package java.util;</div><div>> </div><div>> public class ArrayList<E> implements Iterable<E> {</div><div>>   public Iterator<E> iterator() {</div><div>>     return new Itr();</div><div>>   }</div><div>></div><div>>   private class Itr implements Iterator<E> { }</div><div>> }</div><div><br></div><div>javac will synthesize two constructors for the inner class (e.g. Itr above):</div><div>1. The normal default constructor, with accessibility the same as its holder class - private</div><div>  private java.util.ArrayList$Itr(java.util.ArrayList);</div><div>2. A "bridge constructor". Because the enclosing class needs to access Itr's constructor, but doesn't have accessibility to the private one, so javac futher synthesizes this "bridge constructor" with package accessibility, which simply delegates to the private default one:</div><div>  java.util.ArrayList$Itr(java.util.ArrayList, java.util.ArrayList$1);</div><div><br></div><div>The sole purpose of the "bridge constructor" is to provide accessibility, but if it were only different from the private one in its accessibility, the two constructors won't be distinguishable under JVM's overload resolution rules. So, javac pulls a trick, and appends a marker argument called "access constructor tag" to the argument list of the bridge constructor, e.g. java.util.ArrayList$1 in this example, and always passes a null to this argument.</div><div><br></div><div>In effect, the class of this marker argument never needs to be loaded, because it's never instantiated. But C2 isn't happy about unloaded classes in signature, so it'd refuse to inline any bridge constructors.</div><div><br></div><div>0.320:   17       2 TestC2ArrayListIteratorLoop::sumList</div><div>0.321:              @ 3   java.util.ArrayList::iterator (10 bytes)   inlined (hot)</div><div>0.321:              - @ 6   java.util.ArrayList$Itr::<init> (6 bytes)   unloaded signature classes</div><div>0.321:              @ 8   java.util.ArrayList$Itr::hasNext (20 bytes)   inlined (hot)</div><div>0.321:                @ 8   java.util.ArrayList::access$100 (5 bytes)   inlined (hot)</div><div>0.322:              @ 25   java.lang.Integer::intValue (5 bytes)   inlined (hot)</div><div><br></div><div>With this enhancement, C2 will be able to ignore the unloaded class in the bridge constructor, and inline it:</div><div><br></div><div>0.269:   18       2 TestC2ArrayListIteratorLoop::sumList</div><div>0.269:              @ 3   java.util.ArrayList::iterator (10 bytes)   inlined (hot)</div><div>0.270:                @ 6   java.util.ArrayList$Itr::<init> (6 bytes)   inlined (hot)</div><div>0.270:                  @ 2   java.util.ArrayList$Itr::<init> (26 bytes)   inlined (hot)</div><div>0.270:                  - @ 6   java.lang.Object::<init> (1 bytes)   don't intrinsify this</div><div>0.270:                    @ 6   java.lang.Object::<init> (1 bytes)   inlined (hot)</div><div>0.270:              @ 8   java.util.ArrayList$Itr::hasNext (20 bytes)   inlined (hot)</div><div>0.270:                @ 8   java.util.ArrayList::access$100 (5 bytes)   inlined (hot)</div><div>0.271:              @ 25   java.lang.Integer::intValue (5 bytes)   inlined (hot)</div></div><div><br></div><div>- Kris</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Sep 12, 2016 at 12:13 PM, Vitaly Davidovich <span dir="ltr"><<a href="mailto:vitalyd@gmail.com" target="_blank">vitalyd@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hi all,<div><br></div><div>Vladimir I. and I have been looking at a peculiarity in EA as it relates to eliminating the ArrayList$Itr.  What Vladimir found (and I see it as well) is that ArrayList$Itr::init isn't always inlined due to "unloaded signature classes", e.g.:</div><div><br></div><div><div>@ 6   java.util.ArrayList::iterator (10 bytes)   inline (hot)</div><div>                              @ 6   java.util.ArrayList$Itr::<<wbr>init> (6 bytes)   unloaded signature classes</div></div><div><br></div><div>I tried to dig a bit further into this, and it appears that what's "unloaded" is ArrayList$1.  LogCompilation shows this (which I think is relevant):</div><div><div><bc code='183' bci='6'/></div><div><type id='709' name='void'/></div><div><klass id='827' name='java/util/ArrayList$1' unloaded='1'/></div><div><klass id='821' name='java/util/ArrayList$Itr' flags='2'/></div><div><method id='828' holder='821' name='&lt;init&gt;' return='709' arguments='820 827' flags='4096' bytes='6' iicount='1853'/></div><div><call method='828' count='-1' prof_factor='0.602806' inline='1'/></div><div><inline_fail reason='unloaded signature classes'/></div><div><direct_call bci='6'/></div><div><parse_done nodes='100' live='98' memory='35824' stamp='1.114'/></div><div></parse></div></div><div><br></div><div>It looks like ArrayList$1 is a synthetic class generated by javac because ArrayList$Itr constructor is private (despite the class itself being private).  Here's the bytecode (8u51) of ArrayList::iterator:</div><div><br></div><div><div>public java.util.Iterator<E> iterator();</div><div>    descriptor: ()Ljava/util/Iterator;</div><div>    flags: ACC_PUBLIC</div><div>    Code:</div><div>      stack=4, locals=1, args_size=1</div><div>         0: new           #61                 // class java/util/ArrayList$Itr</div><div>         3: dup</div><div>         4: aload_0</div><div>         5: aconst_null</div><div>         6: invokespecial #62                 // Method java/util/ArrayList$Itr."<<wbr>init>":(Ljava/util/ArrayList;<wbr>Ljava/util/ArrayList$1;)V</div><div>         9: areturn</div><div>      LineNumberTable:</div><div>        line 834: 0</div><div>    Signature: #185                         // ()Ljava/util/Iterator<TE;>;</div></div><div><br></div><div>The only way I can get the Itr allocation removed in my method is by causing some other method that does the same thing to be JIT compiled prior to mine.</div><div><br></div><div>Does anyone have a good idea of what's actually going on here? Why is that synthetic ArrayList$1 such a pest here? It's a bit sad that such a little thing can prevent EA from working in a perfectly good candidate method for it.</div><div><br></div><div>Thoughts?</div><div><br></div><div>Thanks</div></div>
</blockquote></div><br></div>