My understanding of classloading was that a class gets loaded when it is first needed (to put it in a very simple way). Running the following sample with -verbose:class and a modified version of the Iterators class that prints a message when its clinit is called I observed something that I can not really explain though:
public class IteratorsTest
{
public static void main(String[] args)
{
com.google.common.collect.Iterators.forArray(1, 2, 3);
}
}
The (cleaned-up) output is the following:
[Loaded com.google.common.collect.Iterators from file:...]
[Loaded com.google.common.collect.Iterators$1 from file:...]
---------> Iterators <clinit>
Why is Iterators$1 loaded before clinit is called? It is only defined in the clinit, isn't it?
static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR =
new UnmodifiableListIterator<Object>() {
...
}
Which results in the following byte code:
static <clinit>()V
L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "---------> Iterators clinit --------------"**
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
NEW com/google/common/collect/Iterators$1
DUP
INVOKESPECIAL com/google/common/collect/Iterators$1.<init> ()V
L2
PUTSTATIC com/google/common/collect/Iterators.EMPTY_LIST_ITERATOR : Lcom/google/common/collect/UnmodifiableListIterator;
And to confuse me even more I have one more sample (too complex to post here) where the same line of code as in the main above leads to the following output:
[Loaded com.google.common.collect.Iterators from file:...]
---------> Iterators <clinit>
[Loaded com.google.common.collect.Iterators$1 from file:...]
This is actually what I would have expected from the simple test program as well.
I tried to find the answer here https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html , but that didn't really help.
What could be the reason for sometimes the clinit being executed first and sometimes the anonymous class being loaded first?
the class load process contains the follow process.
and now what we focus on resolution and initialization phase for
the reference class loaded take place at resolution phase and the <clint> take place at initialization phase. The order of loading verfication preparation initialization unloading is fixed, however the time when invoke resolution phase is not fixed, it may take place before initialization(correspond your former case) phase and it may also take place after initialization at some scenario(correspond your latter case).
For performance, the HotSpot VM generally waits until class initialization to load and link a class. So if class A references class B, loading class A will not necessarily cause loading of class B (unless required for verification). Execution of the first instruction that references B will cause initialization of B, which requires loading and linking of class B.
Is there a way to trace when the JVM invokes the clinit of the classes? something similar to -verbose:class or -XX:+TraceClassLoading, etc?
I dont know whether there exist some jvm parameter that you can get the time when jvm invokes <clinit> method directly, but there is another way that you can achieve this, using jvm_ti. You can listen some event like methodEntry and then get the time invoking <clinit> method. For more information google jvm_ti.
reference:
Here the summary of the solution for those who don't want to read through all the comments ;)
-noverify
specified. The verifier may cause additional classes to be loaded as also specified here in the JVM Spec. Whether the class is loaded or not, seems to depend on the type of the field to which the object is assigned. More details here. On the other hand, when started with -noverify
, there is no verification and hence the loading of the class only happens at exactly the point where it is first used in the code, which is inside the <clinit>
in my case.<clinit>
without having to modify the byte code. One way is to use the -XX:+TraceClassInitialization
on JDK8. This, however, requires a debug version of the JVM (NOTE: this is not your program started in debug mode but really the VM compiled with debug enabled. A guide for how to build it can be found here). The other way - that only comes with JDK9 though - is to use the new JEP 158: Unified JVM Logging feature and to provide something like the following when starting your program:-Xlog:class+load=info,class+init=info:file=trace.log
(see here for how to get a complete list of tags and arguments)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With