2015-01-13 2 views
6

У нас есть контейнер OSGi с множеством продуктов, работающих внутри, один из которых является нашим продуктом.
У нас есть некоторые тесты производительности, и есть такая странная проблема, что при каждом перезагрузке контейнера OSGi для некоторых наших тестов до 400% будет отклоняться производительность.
Через некоторое тестирование и вещей, которые я был в состоянии отслеживать это вниз к этому методу:Отклонения в производительности для небольшого метода

public static Method getMethodForSlotKey(Class<?> cls, String slotKey, String methodType) { 

    Method[] methods = cls.getMethods(); 

    if (methods != null && methods.length > 0) { 
     for (Method method : methods) { 
      String methName = method.getName(); 
      if (methName.startsWith(methodType)) { 

       IDataAnnotation annot = method.getAnnotation(IDataAnnotation.class); 
       if (annot != null) { 
        String annotSlotKey = annot.SlotKey(); 
        if (annotSlotKey != null && annotSlotKey.equals(slotKey)) { 
         Class<?>[] paramTypes = method.getParameterTypes(); 
         // for now, check length == 1 for setter and 0 for getter. 
         int len = SET_TXT.equals(methodType) ? 1 : 0; 
         if (paramTypes != null && paramTypes.length == len) { 
          return method; 
         } 
        } 
       } 
      } 
     } 
    } 
    return null; 
} 

Этот метод в основном делает отражение и сравнение строк.

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

По-прежнему я не понимаю, почему не кэшированная версия имеет такое высокое отклонение с той лишь разницей, что перезапуск OSGi/JVM? Что именно может произойти во время перезапуска? Существуют ли какие-либо известные проблемы производительности для разных загрузчиков классов? Возможно ли, что в библиотеках среды OSGi будут загружены в другом порядке между перезапусками?

Я ищу ключ, чтобы это имело смысл.

UPDATE

Оказывается, что этот призыв:

Method[] methods = cls.getMethods(); 

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

+2

Некоторые вызовы, основанные на отражении, повторно скомпилированы и оптимизированы JVM, если функция вызывается 15000 раз. После этого они становятся намного эффективнее. Попытайтесь вызвать свою функцию 15 тысяч раз и посмотреть, повышается ли производительность. Если да, это так. Номер после повторного компиляции кода может быть настроен в JVM, но я не помню имена параметров. Также вы можете установить в JVM для выхода из этих задач повторной компиляции. –

+0

Эй, я не нашел этот вариант, может быть, вы можете вспомнить точное имя? – sveri

+0

http://artiomg.blogspot.hu/2011/10/just-in-time-compiler-jit-in-hotspot.html. Я думаю, вы можете установить количество вызовов функций с помощью -XX: CompileThreshold = XXX и распечатать компиляцию и статистику с помощью «XX: + PrintCompilation» и «-XX: -CITime». Если это ответ, я отвечу на него правильно и возьму щедрость ;-) –

ответ

0

Я подозреваю, что это связано с тем, что перезагрузка вашего сервера/контейнера OSGi очищает кэшированный Method[] * в классах, которые вы запрашиваете. Кэш будет очищен, чтобы обеспечить правильную работу в ситуации, когда перезагруженный класс имеет другой открытый API (то есть разные методы), чем старый с тем же именем (это может произойти, например, при загрузке более новой версии)

Когда вы реализуете свой кеш своего метода «обертка», вы обходите это и представляете предположение, что перезагруженные классы будут иметь те же методы, что и ранее загруженные классы с тем же именем. Вероятно, это довольно безопасное предположение для большинства случаев, но не одно, которое вы бы хотели создать для Java.

Исходный код (OpenJDK) Class класс is here. Если вы просмотрите его, вы увидите, что getMethods() звонит privateGetPublicMethods(). Если массив кэшированных publicMethods равен NULL или сделан нулевым значением clearCachesOnClassRedfinition(), тогда классу придется пройти относительно интенсивный процесс поиска всех общедоступных методов в классе и любых суперклассов, на которые он наследует, и интерфейсов, которые он реализует. Затем он кэшируется, и скорость getMethods() вернется к норме.

* строгое, кешированное значение является мягкой ссылкой на массив.

+0

Привет, может быть, я недостаточно четко прояснил и должен обновить свой вопрос, но время ответа остается стабильным после перезапуска OSGi. Таким образом, скорость getMethods не изменяется во время работы среды. Время отклика будет отклоняться только от нескольких перезапусков, но затем остается стабильным во время безотказной работы, вот что меня так смущает. – sveri

+0

Ох. Получил это сейчас, это было странно. Я подумаю об этом –

+0

Прошу прощения за глупый вопрос, я уверен, вы об этом подумали. Из кода, вызывающего ваш метод - возможно ли, что он вызван гораздо чаще, если есть другой порядок, скажем, загрузки библиотеки или запуска потока? Можете ли вы считать вызовы метода каким-то образом (если вы еще этого не сделали). –

0

Кажется, что это еще один случай сложного JIT, который, кажется, постоянно меняется и развивается со временем.Этот ответ может пролить свет на вашу проблему: Java: what is JITC's reflection inflation?

Я считаю, что вы наблюдаете последствия после того, как JIT оптимизирует ваши рефлекторные звонки. Некоторое время (первые вызовы X) будет медленным, пока JIT не решит оптимизировать ваш метод.

Что-то, что вы можете попробовать - проверить эту теорию - это изменить ваш код и поместить нарушающий вызов в фиктивный цикл и выполнить его несколько раз при первом вызове (убедитесь, что вы что-то сделали с результатом вызов заставить java не полностью отключить вызов, если он показывает, что он не имеет последствий). Если это приводит к очень длительному времени выполнения для первого использования, а затем к очень низкой дисперсии последующих вызовов, то теория звучит правильно.

+0

Хм, это в основном то, что происходит во время разминки. Прежде чем запускать тесты, мы можем точно наблюдать это поведение. Звонки берут релятиви долго, но потом, после того, как они казнили какое-то время, они стабилизировались в среднем. Однако, JIT оптимизирует методы по-разному после каждого перезапуска JVM так, чтобы время отклика отличалось после каждого перезапуска JVM? – sveri

+1

Вполне возможно, что JVM оптимизирует вещи по-другому - теоретически он собирает статистику, основанную на методах вызова, а затем решает, как оптимизировать код в соответствии с этими статистическими данными. Если частота выполнения или переданных параметров изменяется после перезапуска JVM, оптимизация может отличаться. Это, по крайней мере, теория, на практике этот процесс оптимизации, по-видимому, слишком сильно изменяется в JVM, настройках JVM и платформах ... – xpa1492

+0

Опять же, настройка всегда была одинаковой, и количество вызовов методов было точно таким же, поэтому я предположил бы, что порядок вызовов методов одинаковый, но я не проверял порядок звонков, так что это может быть одно различие. – sveri

Смежные вопросы