Отражение Java: Почему это настолько медленно?

Можно определить минимальный и максимальный размер пула путем определения MinPoolSize=xyz и/или MaxPoolSize=xyz в строке подключения. Этой причиной проблемы могла быть другая вещь как бы то ни было.

47
задан Jonas 4 November 2011 в 23:08
поделиться

5 ответов

Ваш тест может содержать ошибки. Как правило, хотя JVM может оптимизировать нормальное создание экземпляра, но не может оптимизировать для отражающего варианта использования .

Для тех, кто интересуется, сколько времени было, я добавил фазу разогрева и использовал массив для поддержки созданного объекты (более похожие на то, что может делать настоящая программа). Я запустил тестовый код в своей системе OSX, jdk7 и получил следующее:

Отражение экземпляра заняло: 5180 мс
Обычное создание экземпляра заняло: 2001 мс

Модифицированный тест:

public class Test {

    static class B {

    }

    public static long timeDiff(long old) {
        return System.nanoTime() - old;
    }

    public static void main(String args[]) throws Exception {

        int numTrials = 10000000;
        B[] bees = new B[numTrials];
        Class<B> c = B.class;
        for (int i = 0; i < numTrials; i++) {
            bees[i] = c.newInstance();
        }
        for (int i = 0; i < numTrials; i++) {
            bees[i] = new B();
        }

        long nanos;

        nanos = System.nanoTime();
        for (int i = 0; i < numTrials; i++) {
            bees[i] = c.newInstance();
        }
        System.out.println("Reflecting instantiation took:" + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms");

        nanos = System.nanoTime();
        for (int i = 0; i < numTrials; i++) {
            bees[i] = new B();
        }
        System.out.println("Normal instaniation took: " + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms");
    }


}
40
ответ дан 26 November 2019 в 19:22
поделиться

Отражение происходит медленно по нескольким очевидным причинам:

  1. Компилятор вообще не может выполнять оптимизацию, так как не может иметь реального представления о том, что вы делаете. Это, вероятно, относится и к JIT
  2. Все, что вызывается / создается, должно быть обнаружено (т.е. классы ищутся по имени, методы ищут совпадения и т.д.)
  3. Аргументы необходимо оформить с помощью упаковки / распаковки, упаковки в массивы, Исключений , заключенных в InvocationTargetException и повторно выброшенных и т. Д.
  4. Вся обработка, которая Jon Skeet упоминает здесь .

То, что что-то в 100 раз медленнее , не означает, что это слишком медленно для вас , если предположить, что отражение - это «правильный путь» для вас при разработке вашей программы. Например, Я полагаю, что IDE интенсивно используют отражение, и моя IDE в основном в порядке с точки зрения производительности.

В конце концов, накладные расходы на отражение , вероятно, станут незначительными , когда

1138961] по сравнению с , скажем, синтаксическим анализом XML или доступом к базе данных !

Еще один момент, о котором следует помнить, это то, что микротесты - это заведомо ошибочный механизм для определения того, насколько быстро что-то происходит практика . Как и Тима Бендера , JVM требуется время для "разогрева", JIT может повторно оптимизировать горячие точки кода на лету и т. Д.

накладные расходы на отражение , вероятно, станут незначительными , когда по сравнению с , скажем, синтаксическим анализом XML или доступом к базе данных !

Еще один момент, о котором следует помнить, - это то, что микротесты - это заведомо ошибочный механизм определения того, насколько быстро что-то происходит на практике . Как и Тима Бендера , JVM требуется время для «разогрева», JIT может повторно оптимизировать горячие точки кода на лету и т. Д.

накладные расходы на отражение , вероятно, бледнеют до незначительности , когда по сравнению с , скажем, синтаксическим анализом XML или доступом к базе данных !

Еще один момент, о котором следует помнить, - это то, что микротесты - это заведомо ошибочный механизм определения того, насколько быстро что-то происходит на практике . Как и Тима Бендера , JVM требуется время для «разогрева», JIT может повторно оптимизировать горячие точки кода на лету и т. Д.

43
ответ дан 26 November 2019 в 19:22
поделиться

JITted-код для создания экземпляра B невероятно легкий. По сути, ему нужно выделить достаточно памяти (которая просто увеличивает указатель, если не требуется сборщик мусора), и это все - на самом деле нет кода конструктора, который нужно вызывать; Я не знаю, пропускает ли JIT это или нет, но в любом случае здесь особо нечего делать.

Сравните это со всем, что должно делать отражение:

  • Убедитесь, что существует конструктор без параметров.
  • Проверьте доступность конструктора без параметров
  • Убедитесь, что вызывающий имеет доступ для использования отражения вообще.
  • Определите (во время выполнения), сколько места необходимо выделить
  • Вызов кода конструктора (поскольку он не будет знать заранее, что конструктор пуст)

... и, возможно, другие вещи, которых я не знаю ' Я даже подумал об этом.

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

27
ответ дан 26 November 2019 в 19:22
поделиться

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

    Constructor<B> c = B.class.getDeclaredConstructor();
    c.setAccessible(true);
    for (int i = 0; i < numTrials; i++) {
        c.newInstance();
    }

Normal instaniation took: 47ms
Reflecting instantiation took:718ms

И если вы используете серверную виртуальную машину, она может оптимизировать ее больше, так что она всего в 3-4 раза медленнее. Это вполне типичный спектакль. Статья , связанная с Geo, хорошо читается.

Normal instaniation took: 47ms
Reflecting instantiation took:140ms

Но если вы включите скалярную замену с помощью -XX: + DoEscapeAnalysis, тогда JVM сможет оптимизировать обычное создание экземпляра (это будет 0-15 мс) но рефлексивная реализация остается прежней.

10
ответ дан 26 November 2019 в 19:22
поделиться
  • Reflection was very slow when first introduced, but has been sped up considerably in newer JREs
  • Still, it might not be a good idea to use reflection in an inner loop
  • Reflection-based code has low potential for JIT-based optimizations
  • Reflection is mostly used in connecting loosely-coupled components, i.e. in looking up concrete classes and methods, where only interfaces are known: dependeny-injection frameworks, instantiating JDBC implementation classes or XML parsers. These uses can often be done once at system startup, so the small inefficiency don't matter anyway!
3
ответ дан 26 November 2019 в 19:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: