Будет ли Java встроенный метод (ы) во время оптимизации?

Интересно, достаточно ли JVM / javac, чтобы

// This line... string a = foo(); string foo() { return bar(); } string bar() { return some-complicated-string computation; } 

в

 string a = bar(); 

Или разделите ненужный вызов на foo () в случае релиза (потому что недостижимый код):

 string a = foo(bar()); // bar is the same ... string foo(string b) { if (debug) do-something-with(b); } 

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

javac представит байт-код, который является верным представлением исходной Java-программы, которая генерирует байт-код (за исключением некоторых ситуаций, когда он может оптимизировать: постоянное сгибание и удаление мертвого кода ). Однако оптимизация может выполняться JVM при использовании JIT-компилятора.

Для первого сценария похоже, что JVM поддерживает inlining (см. Здесь « Методы» и см. Здесь пример inlining JVM).

Я не мог найти никаких примеров применения метода javac . Я попробовал скомпилировать несколько примеров программ (аналогично тому, что вы описали в своем вопросе), и ни один из них, казалось, не ввел метод, даже когда он был final . Казалось бы, такие оптимизаторы выполняются JIT-компилятором JVM, а не javac . «Компилятор», упомянутый в разделе « Методы», похоже, является JIT-компилятором javac HotSpot, а не javac .

Из того, что я вижу, javac поддерживает исключение мертвого кода (см. Пример для второго случая) и постоянную фальцовку . При постоянном складывании компилятор будет предварительно вычислять константные выражения и использовать вычисляемое значение вместо выполнения вычисления во время выполнения. Например:

 public class ConstantFolding { private static final int a = 100; private static final int b = 200; public final void baz() { int c = a + b; } } 

компилируется на следующий байт-код:

 Compiled from "ConstantFolding.java" public class ConstantFolding extends java.lang.Object{ private static final int a; private static final int b; public ConstantFolding(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public final void baz(); Code: 0: sipush 300 3: istore_1 4: return } 

Обратите внимание, что байт-код имеет sipush 300 вместо aload s getfield s и iadd . 300 – расчетное значение. Это также относится к private final переменным. Если a и b не являются статическими, итоговый байт-код будет выглядеть следующим образом:

 Compiled from "ConstantFolding.java" public class ConstantFolding extends java.lang.Object{ private final int a; private final int b; public ConstantFolding(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: aload_0 5: bipush 100 7: putfield #2; //Field a:I 10: aload_0 11: sipush 200 14: putfield #3; //Field b:I 17: return public final void baz(); Code: 0: sipush 300 3: istore_1 4: return } 

Здесь также используется sipush 300 .

Во втором случае (исключение мертвого кода) я использовал следующую тестовую программу:

 public class InlineTest { private static final boolean debug = false; private void baz() { if(debug) { String a = foo(); } } private String foo() { return bar(); } private String bar() { return "abc"; } } 

который дает следующий байт-код:

 Compiled from "InlineTest.java" public class InlineTest extends java.lang.Object{ private static final boolean debug; public InlineTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return private void baz(); Code: 0: return private java.lang.String foo(); Code: 0: aload_0 1: invokespecial #2; //Method bar:()Ljava/lang/String; 4: areturn private java.lang.String bar(); Code: 0: ldc #3; //String abc 2: areturn } 

Как вы можете видеть, foo вообще не вызывается в baz потому что код внутри блока if эффективно «мертв».

Sun (теперь Oracle) HotSpot JVM сочетает в себе интерпретацию байт-кода, а также компиляцию JIT. Когда байт-код представлен JVM, код изначально интерпретируется, но JVM будет контролировать байт-код и выделять часто используемые части. Он скрывает эти части в собственный код, чтобы они работали быстрее. Для части байт-кода, которые не используются так часто, эта компиляция не выполняется. Это так же хорошо, потому что компиляция имеет некоторые накладные расходы. Так что это действительно вопрос компромисса. Если вы решите скомпилировать весь байт-код на nativecode, тогда код может иметь очень долгую задержку запуска.

В дополнение к мониторингу байт-кода JVM также может выполнять статический анализ байт-кода, поскольку он интерпретирует и загружает его для дальнейшей оптимизации.

Если вы хотите знать конкретные виды оптимизации, которые выполняет JVM, эта страница в Oracle довольно полезна. В нем описываются методы производительности, используемые в JVM HotSpot.

в том же файле classа javac сможет встроить static и final (другие файлы classов могут изменить встроенную функцию)

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

«Оптимизирующий» JIT-компилятор будет включать оба случая (и, @Mysticial, он может даже встраивать некоторые полиморфные случаи, используя различные формы обмана).

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

javac делает некоторые примитивные вложения, в основном из окончательных / частных методов, прежде всего предназначенных для помощи в некоторых парадигмах условной компиляции.

JVM, скорее всего, будет встроен. В целом лучше всего оптимизировать для чтения человеком. Пусть JVM выполняет оптимизацию времени выполнения.

Эксперт JVM Брайан Гетц говорит, что final не влияет на методы, которые встраиваются.

Если вы выбросили исключение в bar () и распечатаете stacktrace, вы увидите весь путь вызовов … Я думаю, что java чтит их всех.

Второй случай тот же, debug – это просто переменная вашей системы, а не определение, как на C ++, поэтому ее необходимо предварительно оценить.

Возможно, я ошибаюсь, но мое чувство «нет во всех случаях». Поскольку ваша string bar() может быть переопределена перегрузкой другими classами в одном пакете. final методы – хорошие кандидаты, но это зависит от JIT.

Еще одно интересное замечание.

  • Почему mulss занимает всего 3 цикла на Хасуэлле, отличном от таблиц инструкций Агнера?
  • Почему лямбды лучше оптимизируются компилятором, чем простые функции?
  • Каков самый быстрый способ разделить целое на 3?
  • Как планируется x86 uops?
  • для оптимизации цикла
  • Получение всех типов, реализующих интерфейс
  • Что такое оптимизация кода и возврат значения?
  • Minify Html выход приложения ASP.NET
  • Готово ли java final помочь компилятору создать более эффективный байт-код?
  • Помощь оптимизации цикла цикла для окончательного назначения
  • Почему условный ход не уязвим для отказа от ветвления?
  • Interesting Posts
    Давайте будем гением компьютера.