3

I'm looking into JIT behavior on a very simple block of code:

public class PlayWithAssembly {

    public static void main(String[] args) {
        Random random = new Random();
        random.nextInt();
    }
}

Actually, for the purpose of my question the content of the main method is completely irrelevant. I'm running the following code using OpenJDK 10.0.1 on Ubuntu 16.04.5 and with the following command java -Xbatch -XX:+PrintCompilation -XX:CompileThreshold=1000000 -cp target/classes com.xxx.playground.internal.bytecode.PlayWithAssembly

Since CompileThreshold is set to a very high value I wouldn't expect JIT to compile anything, I would rather expect JVM to operate fully in interpreted mode in practice for this example. But when running the above command, I'm getting a following list of methods that have been compiled (all of them are part of JDK):

 47    1    b  3       java.lang.StringLatin1::hashCode (42 bytes)
 50    2    b  3       java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)
 51    3     n 0       jdk.internal.misc.Unsafe::getObjectVolatile (native)   
 55    4    b  3       java.lang.Object::<init> (1 bytes)
 56    5    b  3       java.lang.String::isLatin1 (19 bytes)
 56    6    b  3       java.lang.String::hashCode (49 bytes)
 57    7    b  3       java.lang.String::coder (15 bytes)
 58    8    b  3       java.lang.Math::floorMod (10 bytes)
 59    9    b  3       java.util.ImmutableCollections$SetN::probe (60 bytes)
 62   10    b  1       java.util.ImmutableCollections$Set0::hashCode (2 bytes)
 62   11    b  3       java.lang.String::equals (65 bytes)
 64   12    b  1       java.util.Collections$EmptySet::hashCode (2 bytes)
 65   13    b  3       java.lang.StringLatin1::equals (36 bytes)
 66   14    b  3       java.util.Collections::emptySet (4 bytes)
 66   15    b  3       java.lang.module.ModuleDescriptor$Exports::<init> (10 bytes)
 67   16    b  4       java.lang.StringLatin1::hashCode (42 bytes)
 71    1       3       java.lang.StringLatin1::hashCode (42 bytes)   made not entrant
 72   17    b  3       java.lang.module.ModuleDescriptor$Exports::hashCode (38 bytes)
 73   18    b  3       java.util.Objects::equals (23 bytes)
 73   19    b  3       java.util.Objects::requireNonNull (14 bytes)
 74   20    b  3       java.util.AbstractCollection::<init> (5 bytes)
 76   21    b  3       java.util.AbstractSet::<init> (5 bytes)
 76   22    b  3       java.util.ImmutableCollections$AbstractImmutableSet::<init> (5 bytes)
 77   23    b  1       java.lang.Object::<init> (1 bytes)
 77    4       3       java.lang.Object::<init> (1 bytes)   made not entrant
 81   24    b  1       java.lang.module.ModuleDescriptor::name (5 bytes)
 82   25    b  1       java.lang.module.ModuleReference::descriptor (5 bytes)
 88   26    b  3       java.lang.String::charAt (25 bytes)
 93   27    b  3       java.util.concurrent.ConcurrentHashMap::spread (10 bytes)
 94   28    b  3       java.util.ImmutableCollections$SetN$1::hasNext (47 bytes)
 95   29    b  3       java.util.ImmutableCollections$SetN$1::next (35 bytes)
 96   30    b  3       java.util.Set::of (66 bytes)
 98   31    b  1       java.util.KeyValueHolder::getKey (5 bytes)
 99   32    b  1       java.util.KeyValueHolder::getValue (5 bytes)
100   33    b  3       java.util.ImmutableCollections$MapN::probe (64 bytes)
101   34    b  3       java.util.KeyValueHolder::<init> (21 bytes)
102   35    b  3       java.util.ImmutableCollections$MapN::get (21 bytes)
103   36     n 0       java.lang.Object::hashCode (native)   
103   37    b  3       jdk.internal.module.ModuleReferenceImpl::hashCode (56 bytes)
105   38    b  3       java.util.HashMap::hash (20 bytes)
106   39   !b  3       java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)
112   40     n 0       jdk.internal.misc.Unsafe::compareAndSetLong (native)   
112   41    b  3       java.util.concurrent.ConcurrentHashMap::putIfAbsent (8 bytes)
112   42    b  1       java.lang.module.ResolvedModule::reference (5 bytes)
113   43    b  3       java.util.concurrent.ConcurrentHashMap::addCount (289 bytes)
115    2       3       java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)   made not entrant
115   39   !   3       java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)   made not entrant
115   44    b  3       jdk.internal.misc.Unsafe::getObjectAcquire (7 bytes)
116   45    b  3       java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)
116   46     n 0       jdk.internal.misc.Unsafe::compareAndSetObject (native)   
117   47    b  3       java.util.concurrent.ConcurrentHashMap$Node::<init> (20 bytes)
117   48   !b  3       java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)
120   49    b  3       java.util.concurrent.ConcurrentHashMap::casTabAt (21 bytes)
122   50    b  3       java.util.HashMap::getNode (148 bytes)
124   51    b  3       java.lang.String::length (11 bytes)
125   52    b  3       java.lang.StringLatin1::canEncode (13 bytes)
126   53    b  3       java.util.HashMap::put (13 bytes)
127   54     n 0       java.lang.System::arraycopy (native)   (static)
128   55    b  3       java.util.HashMap$Node::<init> (26 bytes)
128   56    b  3       java.util.HashMap::newNode (13 bytes)
129   57    b  3       java.util.HashMap::afterNodeInsertion (1 bytes)
129   58    b  3       java.util.Optional::ofNullable (15 bytes)
131   59    b  3       java.util.HashMap::get (23 bytes)
132   60    b  3       java.util.HashMap::putVal (300 bytes)
135   61    b  1       java.lang.module.ModuleDescriptor$Exports::source (5 bytes)
135   62    b  1       java.util.Collections$1::hasNext (5 bytes)
136   63    b  3       java.lang.module.ResolvedModule::name (11 bytes)
137   64    b  3       java.util.HashSet::add (20 bytes)
137   65    b  1       java.util.Collections$EmptySet::isEmpty (2 bytes)
138   66    b  3       java.lang.module.ResolvedModule::hashCode (16 bytes)
139   67    b  3       java.lang.module.ModuleDescriptor$Exports::isQualified (18 bytes)
140   68    b  1       java.lang.module.ModuleDescriptor::isAutomatic (5 bytes)
140   69    b  3       java.util.AbstractMap::<init> (5 bytes)
141   70    b  1       java.lang.module.ModuleDescriptor$Exports::targets (5 bytes)
141   71    b  1       java.lang.module.ResolvedModule::configuration (5 bytes)
142   72    b  3       java.util.HashMap::<init> (11 bytes)
142   73    b  3       java.util.ImmutableCollections$Set2$1::hasNext (14 bytes)
143   74    b  4       java.util.ImmutableCollections$SetN$1::hasNext (47 bytes)
148   28       3       java.util.ImmutableCollections$SetN$1::hasNext (47 bytes)   made not entrant
149   75    b  1       java.util.ImmutableCollections$Set1::size (2 bytes)
150   76    b  3       java.lang.Math::min (11 bytes)
152   77    b  3       java.util.AbstractCollection::isEmpty (13 bytes)
153   78    b  4       java.lang.String::hashCode (49 bytes)
160    6       3       java.lang.String::hashCode (49 bytes)   made not entrant
162   79    b  3       java.util.Map::entry (10 bytes)
165   80    b  1       java.lang.module.ModuleDescriptor::isOpen (5 bytes)
167   81    b  1       java.util.HashMap::afterNodeInsertion (1 bytes)
167   57       3       java.util.HashMap::afterNodeInsertion (1 bytes)   made not entrant
168   82    b  3       jdk.internal.module.ModuleBootstrap$2::hasNext (30 bytes)
169   83    b  3       java.util.HashMap::resize (356 bytes)
171   84    b  3       java.util.Collections$UnmodifiableCollection$1::hasNext (10 bytes)
172   85    b  3       jdk.internal.module.ModuleBootstrap$2::next (52 bytes)
173   86    b  3       java.util.HashMap::putIfAbsent (13 bytes)
174   87     n 0       java.lang.Module::addExportsToAllUnnamed0 (native)   (static)
175   88    b  1       java.lang.Module::getDescriptor (5 bytes)
182   78       4       java.lang.String::hashCode (49 bytes)   made not entrant
185   89    b  3       java.lang.StringLatin1::indexOf (61 bytes)
187   23       1       java.lang.Object::<init> (1 bytes)   made not entrant
195   90    b  1       java.lang.Object::<init> (1 bytes)
197   91    b  3       java.lang.String::hashCode (49 bytes) 

I was trying to match these methods with the list of intrinsics but it does not match, so my questions are: why these methods are compiled (and others are not), and do I have any control over it?

MateuszPrzybyla
  • 897
  • 8
  • 17
  • The JVM does a lot of processing as a part of starting up and these methods were called over 10,000 times (or looped 10,000) times. There is little benefit in triggering a method to compile the first time it is used (or before hand) – Peter Lawrey Nov 21 '18 at 19:00
  • @PeterLawrey yes, I'm fully aware of the trade off related to compiling the method right away. I tested it with `CompileThreshold` set to `1` and the code runs for over 4 seconds (with background compilation enabled). Do you have any starting point for digging the topic of what is JVM doing to run my `main` method? – MateuszPrzybyla Nov 21 '18 at 19:07
  • I would look at the `Launcher` class. https://github.com/frohoff/jdk8u-jdk/blob/master/src/share/classes/sun/misc/Launcher.java – Peter Lawrey Nov 22 '18 at 10:33

1 Answers1

6

From the documentation of -XX:CompileThreshold:

This option is ignored when tiered compilation is enabled;
see the option -XX:-TieredCompilation.

So when specifying -XX:-TieredCompilation, most of these entries will go away, however, some entries may still be exempted from the counter based compilation decision.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    Note: there is nothing special from a JIT point of view about the code called and compiled before `main()` is called. IThey are compiled for the same reasons code after main() is called is compiled +1 – Peter Lawrey Nov 21 '18 at 19:01
  • That's it! I added `-XX:-TieredCompilation` and now I feel that I really control the threshold. What now grabbed my attention is that even after setting `CompileThreshold` to `40000` the method `java.lang.StringLatin1::hashCode` still compiles! That means a lot of stuff going on around computing hash codes for Strings happening even before running `main()` method! Actually for my environment that was exactly `45955` invocations. – MateuszPrzybyla Nov 21 '18 at 19:03
  • More about compilation policy: https://stackoverflow.com/a/35614237/3448419 – apangin Nov 21 '18 at 19:09
  • @MateuszPrzybyla well, hashing strings happens all the times. Think system properties (not only those present, each queried key needs to be hashed too), all loaded classes, locale data, file system cache, etc. I don’t know, perhaps, even the string interning resorts to the Java side hash code implementation. Unlikely to happen at startup, but even the garbage collector may hash strings when de-duplication is activated… – Holger Nov 22 '18 at 08:15