Javac также встроен?
Я играл с javap и каким-то очень простым кодом, и это подняло - надеюсь, простой - вопрос.
вот код первый:
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(m1());
System.out.println(m2());
}
private static String m1() {
return new String("foobar");
}
private static String m2() {
String str = "foobar";
return new String(str);
}
}
Теперь я скомпилировал код и посмотрел на вывод (пока опуская -verbose).
$ javap -c Main.class
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3 // Method m1:()Ljava/lang/String;
6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
12: invokestatic #5 // Method m2:()Ljava/lang/String;
15: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
18: return
}
Теперь все это имеет смысл, и я понимаю разные байт-коды, но вопросы, которые пришли мне в голову:
- Я вижу, что "m1" и "m2" упоминаются в вызовах invokestatic, поэтому они как-то вызваны, но я не вижу их фактических выходных данных байт-кода в вызове javap!
- Теперь они встроены или просто не появляются? И если так, то почему?
Опять же, этот вопрос просто представляет интерес для того, как javac обрабатывает все это внутренне. Спасибо!
2 ответа
Они есть, но используемые по умолчанию флаги не показывают их, поскольку они являются закрытыми методами. Чтобы увидеть определение как для m1, так и для m2, используйте
javap -p -c .\Main.class
Это покажет всех внутренних членов, включая частных и публичных. Это то, что вы получите, если будете использовать вышеуказанную команду.
PS C:\Users\jbuddha> javap -p -c .\Main.class
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3 // Method m1:()Ljava/lang/String;
6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
12: invokestatic #5 // Method m2:()Ljava/lang/String;
15: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
18: return
private static java.lang.String m1();
Code:
0: new #6 // class java/lang/String
3: dup
4: ldc #7 // String foobar
6: invokespecial #8 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: areturn
private static java.lang.String m2();
Code:
0: ldc #7 // String foobar
2: astore_0
3: new #6 // class java/lang/String
6: dup
7: aload_0
8: invokespecial #8 // Method java/lang/String."<init>":(Ljava/lang/String;)V
11: areturn
}
Javac не делает никаких методов встраивания. Это оставляет JVM ответственным за эту и другую оптимизацию во время выполнения. JVM (по крайней мере, Oracle) очень хороша для встраивания и будет работать на нескольких уровнях. Он может даже встроить некоторые полиморфные вызовы методов, если они будут признаны мономорфными во время выполнения (т. Е. На конкретном сайте вызова он пытается обнаружить, когда существует только одна возможная реализация метода, которая может быть вызвана, даже если метод переопределяемый).
Вы также можете использовать постпроцессор, такой как ProGuard, чтобы встроить и оптимизировать код Java после компиляции.
PS создание новых объектов String, например:
return new String("foobar");
расточительно и всегда ненужно. Вы можете просто сделать:
return "foobar";