avap是jdk自帶的一個工具在jdk安裝目錄的/bin下面可以找到,可以對代碼反編譯,也可以查看java編譯器生成的字節碼,對代碼的執行過程進行分析,了解jvm內部的工作。
下面列舉javap命令的常用options及其功能描述,更多功能的使用請自行Google,樓主不做贅述。
用法摘要
-help 幫助
-l 輸出行和變量的表
-public 只輸出public方法和域
-protected 只輸出public和protected類和成員
-package 只輸出包,public和protected類和成員,這是默認的
-p -private 輸出所有類和成員
-s 輸出內部類型簽名
-c 輸出分解后的代碼,例如,類中每一個方法內,包含java字節碼的指令,
-verbose 輸出棧大小,方法參數的個數
-constants 輸出靜態final常量
實例分析
javap命令分解一個class文件,它根據options來決定到底輸出什么。如果沒有使用options,那么javap將會輸出該class文件中的包,類里的protected和public域以及類里的所有方法。javap將會把它們輸出在標準輸出上。來看這個例子,先編譯(javac)下面這個類。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package com.thundersoft.metadata.test.kafka; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import org.junit.Test; import java.util.Arrays; import java.util.Properties; public class KafkaTest { @Test public void testProducer() { Properties props = new Properties(); props.put( "bootstrap.servers" , "192.168.204.30:9092" ); props.put( "acks" , "all" ); props.put( "retries" , 0 ); props.put( "batch.size" , 16384 ); props.put( "linger.ms" , 1 ); props.put( "buffer.memory" , 33554432 ); props.put( "key.serializer" , "org.apache.kafka.common.serialization.StringSerializer" ); props.put( "value.serializer" , "org.apache.kafka.common.serialization.StringSerializer" ); Producer<String, String> producer = new KafkaProducer<>(props); for ( int i = 0 ; i < 100 ; i++) { producer.send( new ProducerRecord<String, String>( "my-topic" , Integer.toString(i), Integer.toString(i))); } producer.close(); } @Test public void testKafkaConsumer() { Properties props = new Properties(); props.put( "bootstrap.servers" , "192.168.204.30:9092" ); props.put( "group.id" , "test" ); props.put( "enable.auto.commit" , "true" ); props.put( "auto.commit.interval.ms" , "1000" ); props.put( "key.deserializer" , "org.apache.kafka.common.serialization.StringDeserializer" ); props.put( "value.deserializer" , "org.apache.kafka.common.serialization.StringDeserializer" ); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList( "my-topic" )); while ( true ) { ConsumerRecords<String, String> records = consumer.poll( 100 ); for (ConsumerRecord<String, String> record : records) System.out.printf( "offset = %s, key = %s, value = %s%n" , record.topic(), record.key(), record.value()); } } public static void main(String[] args) { int a = 2 ; int b = 3 ; int sum = a*b; System.out.println(sum); } } |
在命令行上鍵入javap KafkaTest后,輸出結果如下
1
2
3
4
5
6
|
public class com.thundersoft.metadata.test.kafka.KafkaTest { public com.thundersoft.metadata.test.kafka.KafkaTest(); public void testProducer(); public void testKafkaConsumer(); public static void main(java.lang.String[]); } |
結合代碼分析編譯器執行過程
1
2
3
4
5
6
|
public static void main(String[] args) { int a = 2 ; int b = 3 ; int sum = a*b; System.out.println(sum); } |
在命令行上鍵入javap -c KafkaTest后,輸出結果如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static void main(java.lang.String[]); Code: 0 : iconst_2 1 : istore_1 2 : iconst_3 3 : istore_2 4 : iload_1 5 : iload_2 6 : imul 7 : istore_3 8 : getstatic # 47 // Field java/lang/System.out:Ljava/io/PrintStream; 11 : iload_3 12 : invokevirtual # 54 // Method java/io/PrintStream.println:(I)V 15 : return |
如上面代碼所,iconst_2 與iconst_3分別代表常量2,3 。istore_1 ,istore_2 分別代表定義兩個普通變量,iload_1 , iload_2 分別表示加載istore_1,istore_2 兩個變量到數據棧中,imul表示兩個變量做乘法運算,結果賦值給變量istore_3,最后將結果輸出,程序返回。
在分析這段簡單代碼的過程中,樓主發現了一個jvm編譯命令的網站,分享出來jvm指令。
總結
樓主在上面做了一個簡單的代碼分析的過程,希望可以幫助到有緣人。javap可以用于反編譯和查看編譯器編譯后的字節碼。一般用到的不多,不過平時用javap -c比較多,該命令用于列出每個方法所執行的JVM指令,用來解決比較棘手的邏輯出錯的bug是個不錯的選擇。另外通過字節碼和源代碼的對比,深入分析java的編譯原理及代碼執行過程,解決各種Java原理級別的問題。