2011-01-04 4 views
4

Я был microbenchmarking какой-то код (пожалуйста, будь красивым) и наткнулся на эту головоломку: при чтении поля с использованием отражения вызов метода getter быстрее, чем чтение поля.Java-отражение При вызове метода результат получается быстрее, чем поля?

Простой тестовый класс:

private static final class Foo { 
    public Foo(double val) { 
     this.val = val; 
    } 
    public double getVal() { return val; } 
    public final double val; // only public for demo purposes 
} 

У нас есть два отражения:

Method m = Foo.class.getDeclaredMethod("getVal", null); 
Field f = Foo.class.getDeclaredField("val"); 

Теперь я называю два отражения в цикле, invoke о методе, и get на поле. Первый запуск выполняется для разогрева виртуальной машины, второй запуск выполняется с помощью итераций 10M. Вызов метода последовательно на 30% быстрее, но почему? Обратите внимание, что getDeclaredMethod и getDeclaredField: не, вызываемые в цикле. Они вызывается один раз и выполняется на одном и том же объекте в цикле.

Я также пробовал некоторые незначительные вариации: сделал поле незавершенным, переходным, непубличным и т. Д. Все эти комбинации привели к статистически подобной производительности.

Редактировать: Это на WinXP, Intel Core2 Duo, Sun JavaSE build 1.6.0_16-b01, работающий под jUnit4 и Eclipse.

+0

У вас возникли проблемы? –

+0

Это предложение выделено жирным шрифтом. – omerkudat

ответ

3

Мое образованное предположение было бы разницей в том, как реализованы методы getDeclaredField и getDeclaredMethod: хотя каждый раз, когда он вызывается, getDeclaredField должен проверять тип и размер переменной, чтобы возвращать фактический объект или примитивный тип, getDeclaredMethod вернет указатель на один и тот же метод, который будет заботиться обо всем остальном статически.

Edit:

Мое объяснение аналогично: метод содержится в памяти только один раз для каждого класса, в то время как каждый объект экземпляра может иметь различные значения свойств. Когда вы получаете значение свойства, выполняя вызов метода (по-прежнему используя только указатель метода), компилятор оптимизировал метод для доступа к параметру, зная точную иерархию классов и т. Д., Когда вы получаете значение свойства, используя «get ", вы позволяете отражениям выполнять работу метода getter, и, очевидно, не может быть никакой оптимизации компилятора.

+0

Я добавил разъяснение к вопросу, эти методы вызывают один раз, вне цикла.Цикл содержит только рефлексивные вызовы. – omerkudat

+0

Спасибо, что поняли. Я отредактировал свой ответ соответственно. – weltraumpirat

+0

Не будет ли макет памяти объекта одинаковым для каждого экземпляра? Я бы предположил, что это может привести к оптимизации, когда поле читает определенное смещение от адреса объекта. Обратите внимание, что класс является окончательным, поэтому макеты подкласса не мешают. – omerkudat

0

это также означает, что double d = Foo.getVal() на 30% быстрее, чем double d = Foo.val?

+1

Нет. Определенно нет. И отражение гораздо * медленнее, чем простое назначение. –

+0

Надеюсь, это не так, потому что я могу представить, как люди мчатся, чтобы заменить своих геттеров открытыми полями, и все мы знаем, что это будет плохая вещь. – omerkudat

1

В вашем микрообъекте вызов метода выполняется быстрее из-за оптимизации, создаваемой JVM/Hotspot с вашим циклом.

Изменение microbenchmak:

Сделайте петлю, в которой: прочитать значение по Reflection, затем увеличить 1 (например), а затем присвоить то же поле с помощью отражения. И вне цикла, сделайте окончательное чтение и System.out.println ...

Выполните два варианта (Поле против метода), и вы увидите, что реальная разница прямо противоположна: вызовы метода на самом деле 30 -40% медленнее.

С уважением

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