Проблемы, упомянутые Andreas , и непредсказуемость JIT верны, но еще одна проблема - загрузчик классов :
Первый вызов fOne
радикально отличается от последних, потому что именно это делает первый вызов System.out.println
, что означает, что загрузчик классов будет из кэша диска или файловой системы (обычно он кэшируется). все классы, которые необходимы для печати текста. Передайте JVM параметр -verbose:class
, чтобы увидеть, сколько классов фактически загружено во время этой маленькой программы.
Я заметил похожее поведение при запуске модульных тестов - первый тест для вызова большого фреймворка занимает гораздо больше времени (в случае с Guice около 250 мс на C2Q6600), даже если тестовый код будет таким же, потому что первый вызов - это когда сотни классов загружаются загрузчиком классов.
Поскольку ваша примерная программа очень коротка, накладные расходы, вероятно, связаны с очень ранними JIT-оптимизациями и загрузкой классов. Сборщик мусора, вероятно, даже не запустится до завершения программы.
Обновление:
Теперь я нашел надежный способ узнать, что действительно тратит время. Никто еще не обнаружил это, хотя это тесно связано с загрузкой классов - это было динамическое связывание нативных методов !
Я изменил код следующим образом, чтобы журналы показывали, когда тесты начинаются и заканчиваются (путем просмотра, когда загружены эти пустые классы маркеров).
TestIO t = new TestIO();
new TestMarker1();
t.fOne();
t.fTwo();
t.fOne();
t.fTwo();
new TestMarker2();
Команда для запуска программы с правильными параметрами JVM , которые показывают, что на самом деле происходит:
java -verbose:class -verbose:jni -verbose:gc -XX:+PrintCompilation TestIO
А на выходе:
* snip 493 lines *
[Loaded java.security.Principal from shared objects file]
[Loaded java.security.cert.Certificate from shared objects file]
[Dynamic-linking native method java.lang.ClassLoader.defineClass1 ... JNI]
[Loaded TestIO from file:/D:/DEVEL/Test/classes/]
3 java.lang.String::indexOf (166 bytes)
[Loaded TestMarker1 from file:/D:/DEVEL/Test/classes/]
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
100
Time taken by 'fOne' ... 155354
100
Time taken by 'fTwo' ... 23684
100
Time taken by 'fOne' ... 22672
100
Time taken by 'fTwo' ... 23954
[Loaded TestMarker2 from file:/D:/DEVEL/Test/classes/]
[Loaded java.util.AbstractList$Itr from shared objects file]
[Loaded java.util.IdentityHashMap$KeySet from shared objects file]
* snip 7 lines *
И причина такой разницы во времени такова: [Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
Мы также видим, что JIT-компилятор не влияет на этот тест. Есть только три метода, которые скомпилированы (например, java.lang.String::indexOf
в приведенном выше фрагменте), и все они происходят до вызова метода fOne
.