2017-01-15 12 views
5

Мое понимание загрузки классов состояло в том, что класс загружается, когда он в первую очередь необходим (чтобы сделать его очень простым). Запуск следующего образца с -verbose: класс и модифицированную версию класса Итераторов, который печатает сообщение, когда его clinit называется я заметил что-то, что я не могу объяснить, хотя:Загрузка, связывание и инициализация. Когда класс загружается?

public class IteratorsTest 
{ 
    public static void main(String[] args) 
    { 
     com.google.common.collect.Iterators.forArray(1, 2, 3); 
    } 
} 

The (очищено вверх) выход следующий:

[Loaded com.google.common.collect.Iterators from file:...] 
[Loaded com.google.common.collect.Iterators$1 from file:...] 
---------> Iterators <clinit> 

Почему Итераторы $ 1 загружены до того, как климат называется? Он определен только в клинике, не так ли?

static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR = 
     new UnmodifiableListIterator<Object>() { 
    ... 
    } 

Какие результаты в следующем байт-код:

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; 

И запутать меня еще больше у меня есть еще один образец (слишком сложен, чтобы разместить здесь), где та же строка кода, как и в основной выше, приводит к следующему выходу:

[Loaded com.google.common.collect.Iterators from file:...] 
---------> Iterators <clinit> 
[Loaded com.google.common.collect.Iterators$1 from file:...] 

Это на самом деле то, чего я ожидал от простой тестовой программы.

Я попытался найти ответ здесь https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html, но это действительно не помогло.

  • Что может быть причиной того, что иногда климат выполняется первым, а иногда и первым загружается анонимный класс?
  • Есть ли способ отслеживать, когда JVM вызывает климат классов? что-то похожее на -verbose: class или -XX: + TraceClassLoading и т. д.?
+0

Новая инфраструктура регистрации openjdk9 позволяет довольно мелкомасштабную разбивку множества внутренних компонентов, включая загрузку и инициализацию классов. возможно, это предоставит необходимую вам информацию. – the8472

+0

Хорошо, я читал о новых функциях регистрации, но еще не пробовал их. Возможно, стоит сделать пробный прогон. Благодаря! –

+0

Хотя я нашел это, я хотел знать, помог бы JDK9, и да, действительно, это выполнило бы эту работу. Я начал с -Xlog: class + init = info, class + load = info: file = trace.log (btw: есть ли где-нибудь полный список доступных селекторов? Что здесь представлено http://openjdk.java.net/ jeps/158 на самом деле недостаточно). PS: это был ваш комментарий здесь http://stackoverflow.com/questions/39321345/how-do-measure-jvm-startup-time, где я читал об этом в первую очередь. –

ответ

3

Вот краткое изложение решения для тех, кто не хочет, чтобы прочитать все комментарии;)

  1. Разница в порядке выполнения была вызвана одной из пусковых установок, имеющих -noverify. Верификатор может вызвать загрузку дополнительных классов, также указанных здесь в JVM Spec. Является ли класс загруженным или нет, похоже, зависит от типа поля, которому назначен объект. Подробнее here. С другой стороны, при запуске с -noverify проверка не ведется, и, следовательно, загрузка класса происходит только в том месте, где оно впервые используется в коде, которое находится внутри <clinit> в моем случае.
  2. Есть способы отслеживать вызов <clinit> без необходимости изменения байтового кода. Один из способов - использовать -XX:+TraceClassInitialization на JDK8. Это, однако, требует отладочной версии JVM (ПРИМЕЧАНИЕ: это не ваша программа, запущенная в режиме отладки, но на самом деле VM скомпилирована с включенной отладкой. Руководство по ее созданию можно найти here). Другой способ - это приходит только с JDK9, хотя - это использование нового JEP 158: Unified JVM Logging feature и обеспечить что-то вроде следующего при запуске программы:
    -Xlog:class+load=info,class+init=info:file=trace.log (см here о том, как получить полный список тегов и аргументов)
5

Что может быть причиной иногда clinit выполняется первым, а иногда анонимный класс загружается первым?

Процесс загрузки класса содержит следующий процесс.

  • загрузки
  • связывая
    • проверка
    • препарат
    • Разрешение
  • инициализации
  • Использование
  • выгрузить

и что теперь мы сосредоточимся на решении и инициализации фазы для опорного класса загружен иметь место в разрешение фазы и < Clint> иметь место при инициализации фазы. Порядок загрузки инициализации подготовки verfication разгрузки фиксируется, однако время, когда Invoke разрешения фаза не является фиксированными, она может иметь место до инициализации (соответствует вашему первому случаю) фазу, а также может иметь место после инициализации в некоторых сценариях (соответствует вашему последнему случаю).

Для достижения эффективности, HotSpot VM обычно ожидает инициализации класса для загрузки и соединения класса. Поэтому, если класс A ссылается на класс B, загрузка класса A не обязательно приведет к загрузке класса B (если это не требуется для проверки). Выполнение первой инструкции, которая ссылается на B, приведет к инициализации B, которая требует загрузки и соединения класса B.

Есть ли способ отслеживать, когда JVM вызывает клинику классов? что-то похожее на -verbose: class или -XX: + TraceClassLoading и т. д.?

Я не знаю, существует ли какой-либо параметр Jvm что вы можете получить время, когда JVM вызывает < clinit> метода напрямую, но есть еще один способ, что вы можете достичь этого, используя jvm_ti. Вы можете прослушать какое-то событие, например methodEntry, а затем получить время, вызывающее метод < clinit>. Для получения дополнительной информации google jvm_ti.

ссылка:

+0

Я также работал с -XX: + TraceClassResolution, но нет выхода для Iterators $ 1 перед загрузкой класса. Что заставляет вас думать, что время для решения не фиксировано? Насколько я понимаю, это инструкции из главы 5.4.3 спецификации JVM, которые приводят к разрешению символических ссылок. Однако эти инструкции вызываются только во время клиники (следовательно, я ожидал бы разрешения и нагрузки в этой точке). Мне действительно нужно понять, что вызывает загрузку Итераторов $ 1 до того, как вызывается клиника Итераторов. –

+1

@Haasip Satang [спецификация jvm 5.4 ссылки] (https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.3) говорит: 'a Virtual Virtual Реализация машины может решить разрешить каждую символическую ссылку в классе или интерфейсе отдельно, когда она используется («ленивое» или «позднее» разрешение), или разрешать их все сразу, когда класс проверяется («нетерпеливый» или «статический» «разрешение». –

+0

Да, но поскольку мы говорим о той же виртуальной машине (не в разных реализациях), я бы принял такое же поведение. Но даже если JVM решит выбрать разные подходы для одной и той же строки кода, я хотел бы понять причину этого. Это, конечно, не random.It всегда загружает класс сначала в программе A, а в B-клинике сначала вызывается.Метод с одной строкой кода в обеих программах точно такой же. Вопрос в том, почему JVM выбирает другую стратегию. ПРИМЕЧАНИЕ. Я занимаюсь некоторыми исследованиями в области недетерминированности в JVM, поэтому мне нужно знать эту деталь низкого уровня. –