Почему java.lang.Object.getClass () (и отражение) медленнее, чем обычно?

Мы сталкиваемся с некоторыми странными проблемами производительности JVM.

У нас есть большой и несколько непрозрачный компонент графического интерфейса (таблица Actuate Formula 1).

Если мы инициализируем все это из потока диспетчеризации событий (как и следовало бы), мы обнаружим, что код работает значительно медленнее (перетаскивание мыши для выбора ячеек приводит к заметной задержке).

Если мы инициализируем его в первый раз в основном потоке запуска и только потом начнем использовать его в EDT, он будет работать намного быстрее.

Когда я смотрю на то, почему он работает медленно, используя профилировщик, все время вызываются следующие методы:

  • java.lang.Object.getClass ()
  • java.lang.reflect.Array.newInstance (Класс, интервал)
  • java.lang.Class.getComponentType ()
  • java.lang.Thread.currentThread ()

Мы используем 64-битную JVM Sun Hotspot в Windows 7 (ту, что поставляется с JDK).

Кто-нибудь знает причину, по которой вышеуказанные методы могут работать намного медленнее, чем обычно?

Я думаю, что, может быть, это как-то связано с порядком загрузки классов ... это разумная теория? Кто-нибудь знает какие-либо другие способы диагностики, почему эти вызовы методов могут занимать много времени?

Прикрепил два скриншота из профилировщика. В обоих случаях все, что я делал, - это перетаскивал мышь по ячейкам электронной таблицы во время работы профилировщика. Таким образом, он просто обновляет компонент графического интерфейса и ничего больше не делает.

Первый тратит много времени на метод под названием «releaseLock ()». По какой-то причине это занимает много времени, потому что "getComponentType ()" занимает гораздо больше времени, чем обычно.

releaseLock

Второй - после того, как я «взломал», чтобы убрать стоимость «releaseLock ()», но теперь он просто тратит много времени на «getLock ()» из-за того, что getClass () и currentThread () занимают намного больше времени, чем обычно. :

getLock

Но важно то, что если я просто изменю порядок инициализации кода, выполнение этого кода не займет много времени (он даже не отображается в профилировщике).

Если бы мне пришлось оптимизировать getLock (), приложение все равно работало бы намного медленнее. Проблема действительно в том, что такие методы, как getClass (), занимают слишком много времени. Это невозможно компенсировать - getClass () вызывается слишком во многих местах!

Разница в производительности заметна даже без запущенного профилировщика.

Также помните, что мы не можем изменить этот код - это внешний компонент. Задача состоит в том, чтобы объяснить, почему в одних обстоятельствах код выполняется намного медленнее, чем в других.


person Paul Hollingsworth    schedule 09.01.2013    source источник
comment
Можете выложить скриншот профилирования? Кроме того, профилирование может существенно повлиять на время выполнения. Можете ли вы исключить эти методы из профилирования и посмотреть, есть ли другие причины вашего отставания?   -  person jlordo    schedule 09.01.2013
comment
Я не знаю, как исключить эти методы из профилирования, но, как вы можете видеть, он использует выборку и показывает разбивку. Я думаю, что то, что показывает профилировщик, верно - дело не в том, что выполняется другой код, просто по какой-то причине эти вызовы методов, которые обычно довольно недорогие (Object.getClass ()), теперь занимают много времени.   -  person Paul Hollingsworth    schedule 09.01.2013
comment
Соответствует ли этот результат? Потому что, если это происходит только один раз, это может легко стать проблемой при отборе проб (неудача в выборе времени отбора пробы).   -  person Joachim Sauer    schedule 09.01.2013
comment
Это последовательно - всегда происходит одинаково.   -  person Paul Hollingsworth    schedule 09.01.2013
comment
Что вы делаете в getLock () и releaseLock ()? Кроме того, когда вы запускаете Thread.currentThread (). GetContextClassLoader () в своем «основном» потоке и в потоке «диспетчер событий», являются ли они одной и той же реализацией ClassLoader реализация? Является ли CL для EDT заметным из-за того, что он находится на потенциально медленном пути ввода-вывода для загрузки классов (например, внутри Jar, а не в файловой системе ?!)   -  person David Bullock    schedule 09.01.2013
comment
Дэвид, интересная идея ... это один и тот же экземпляр ClassLoader для обоих потоков: sun.misc.Launcher$AppClassLoader@35a16869. Мне интересно, не портит ли что-то еще происходящее в потоке отправки событий.   -  person Paul Hollingsworth    schedule 09.01.2013
comment
getLock и releaseLock используются внутри компонента в том, что мне кажется сильно ошибочной многопоточностью. Каждый метод окружен функциями getLock () и releaseLock (). Они создают отдельный поток, который может оценивать формулы, и используют его для поддержки сценария потока вычислений, выполняющегося одновременно с EDT. Я бы предпочел, чтобы они вообще не использовали никаких потоков. Я думаю, что это отвлекающий маневр: код не очень красивый, но он оказывается медленным только тогда, когда Object.getClass () работает медленно. Object.getClass () вообще не требует времени.   -  person Paul Hollingsworth    schedule 09.01.2013
comment
Чтобы уточнить: даже если я избавлюсь от вызовов getLock и releaseLock с помощью некоторого воображаемого хакерства, весь компонент графического интерфейса будет работать очень медленно во многих других отношениях. Профилировщик всегда отслеживает эти вызовы до Object.getClass () и т. Д.   -  person Paul Hollingsworth    schedule 09.01.2013
comment
Я думаю, вам нужно увеличить частоту выборки, чтобы получить здесь лучшее изображение - как я интерпретирую эти снимки экрана, во время выполнения каждого из этих методов (Object.getClass и т. Д.) Был один образец, и время, указанное в каждом из них. Метод - это оценка, основанная на соотношении образцов в методе к общему количеству образцов. Но 144 образца за 13 секунд - это слишком мало, чтобы получить хорошее представление о ваших узких местах в производительности.   -  person Alex    schedule 09.01.2013
comment
Привет, Алекс, я понимаю, но по ряду причин доверяю тому, что говорит мне профилировщик. В основном тот факт, что эти вызовы занимают гораздо больше времени, объясняет, почему так много разрозненного кода выполняется так медленно. Количество вызовов одинаково в быстром и медленном случае - ›он не выполняет больше кода, просто код, который он выполняет, выполняется намного медленнее. Вкратце: я не думаю, что профилировщик ошибся в измерении.   -  person Paul Hollingsworth    schedule 10.01.2013
comment
Содержимое вашего пути к классам - есть ли какие-либо из них на удаленных дисках (например, смонтированы через CIFS / SMB)? Интересно, приостанавливается ли EDT во время сетевой активности, когда классы были изначально загружены в EDT. Простым тестом может быть копирование вашего пути к классам на локальный диск, чтобы увидеть, меняет ли он воспринимаемую отзывчивость.   -  person dashrb    schedule 11.01.2013


Ответы (1)


Вы можете попробовать взять несколько смешанных (java + native) jstack -m дамп потоков приложения под нагрузкой.

Это, вероятно, даст намек на то, что происходит под капотом Object.getClass().

См. Также Если профилировщик не является ответом , какие еще варианты у нас есть?.

Также обратите внимание на Как выглядит дамп потока, когда JVM проводит время в GC. Потому что обычные дампы потоков JVM не всегда могут быть идеально точными, поскольку они происходят только на точки безопасности.

person Vadzim    schedule 10.01.2013