1. 程式人生 > >第三章 類檔案結構與javap的使用

第三章 類檔案結構與javap的使用

注:本文主要參考自《深入理解java虛擬機器(第二版)》

1、javap的使用與類檔案結構

使用過程:

java原始碼:

 1 package compile;
 2 /**
 3  * class位元組碼
 4  */
 5 public class TestClass {
 6     private int m;
 7     
 8     public int inc() {
 9         return m + 1;
10     }
11 }
View Code

在硬碟上找到java原始檔所在目錄(eg.E:\Java\workspaceOfMyBatis3\baseUtil\src\compile)

開啟命令視窗,執行"javac -g TestClass.java"生成TestClass.class位元組碼檔案,然後使用"javap -c TestClass > TCC.txt"將位元組碼檔案的處理結果輸出到TCC.txt中。

開啟TCC.txt,如下:

Compiled from "TestClass.java"
public class compile.TestClass extends java.lang.Object{
    public compile.TestClass();
      Code:
       0:    aload_0
       
1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public int inc(); Code: 0: aload_0 1: getfield #2; //Field m:I 4: iconst_1 5: iadd 6: ireturn }
View Code

說明:

  • javac -g TestClass.java
    • -g:生成所有的除錯資訊,包括區域性變數名
      和行號資訊。
  • javap -c TestClass > TCC.txt,對於javap常用的引數:
    • -c:輸出位元組碼Code
    • -l(小寫L):輸出Code、LineNumberTable與LocalVariableTable
    • -s:輸出方法簽名(方法的接收引數列表和返回值)
    • -verbose:包含-c、-l以及輸出class檔案的編譯版本,常量池,Stack, Locals, Args_size
  • 對於javap而言,常用的就是-c或-verbose  

這裡列出使用"javap -verbose TestClass > TCV.txt的結果:

Compiled from "TestClass.java"
public class compile.TestClass extends java.lang.Object
  SourceFile: "TestClass.java" /* 原始檔名稱 */
  minor version: 0        /* 次版本號 */
  major version: 50        /* 主版本號,50-->jdk6 */
  Constant pool:        /* 常量池:存放所有的方法名、field名、方法簽名(方法引數+返回值)、型別名、class檔案中的常量值 */
    const #1 = Method    #4.#18;    //  java/lang/Object."<init>":()V
    const #2 = Field    #3.#19;    //  compile/TestClass.m:I
    const #3 = class    #20;    //  compile/TestClass    
    const #4 = class    #21;    //  java/lang/Object    
    const #5 = Asciz    m;                                /*field名*/
    const #6 = Asciz    I;                                /*型別名*/
    const #7 = Asciz    <init>;                            /*方法名(構造器)*/
    const #8 = Asciz    ()V;                             /*方法簽名(方法引數+返回值)*/
    const #9 = Asciz    Code;
    const #10 = Asciz    LineNumberTable;                /*class檔案中的常量值:Java原始碼的行號與位元組碼指令對應關係*/
    const #11 = Asciz    LocalVariableTable;                /*class檔案中的常量值:區域性變量表*/
    const #12 = Asciz    this;
    const #13 = Asciz    Lcompile/TestClass;;            /*當前類的型別"Lxxx;"表示xxx引用型別*/
    const #14 = Asciz    inc;                            /*方法名*/
    const #15 = Asciz    ()I;                            /*方法簽名(方法引數+返回值)*/
    const #16 = Asciz    SourceFile;                        /*class檔案中的常量值:原始檔名稱*/
    const #17 = Asciz    TestClass.java;                    /*class檔案中的常量值:原始檔名稱*/
    const #18 = NameAndType    #7:#8;//  "<init>":()V
    const #19 = NameAndType    #5:#6;//  m:I
    const #20 = Asciz    compile/TestClass;                /*型別名*/
    const #21 = Asciz    java/lang/Object;                /*型別名*/

{
    public compile.TestClass();
      Code:    /* 方法位元組碼 */
       /* Stack:運算元棧的深度(這個值就是類載入階段為運算元棧分配的深度)
        * Locals:區域性變數的分配空間(單位是slot,不是個數),對於double和long這兩個64bit的,需要兩個slot,對於其他<=32bit的,只需要一個slot
        * Args_size:方法引數的個數,包括方法引數、this(this只針對例項方法,static方法不會自動新增this)
        */
       Stack=1, Locals=1, Args_size=1 
       0:    aload_0    /*將第0個Slot中的引用型別的本地變數推到運算元棧頂,這裡就是LocalVariableTable的this*/
       1:    invokespecial    #1; //Method java/lang/Object."<init>":()V  /* invokespecial #1:呼叫#1常量代表的方法,這裡就是super(),當前棧頂的元素作為該方法#1的接收者 */
       4:    return    /*返回該方法,該方法的返回值為Void,執行了return指令,方法結束*/
       
      LineNumberTable: /* Java原始碼的行號與位元組碼指令對應關係 */
       line 5: 0

      LocalVariableTable: /* 區域性變量表 */
       Start  Length  Slot  Name   Signature
       0      5      0    this       Lcompile/TestClass;


    public int inc();
      Code:
       Stack=2, Locals=1, Args_size=1
       0:    aload_0                            /*將第0個Slot中的引用型別的本地變數推到運算元棧頂,這裡就是LocalVariableTable的this*/
       1:    getfield    #2; //Field m:I        /*getfield #2:獲取常量表中定義的#2例項(即例項m),然後將m推到運算元棧頂*/
       4:    iconst_1                        /*向棧頂壓入一個int常量1*/
       5:    iadd                            /*將棧頂的兩個元素相加(這裡是1和m),然後將結果壓入棧頂*/
       6:    ireturn                            /*從當前方法返回棧頂的int型數值結果*/
      LineNumberTable: 
       line 9: 0

      LocalVariableTable: 
       Start  Length  Slot  Name   Signature
       0      7      0    this       Lcompile/TestClass;
}
View Code

說明:

  • 上述檔案中/*xxx*/這樣的註釋是我新增的,//這樣的註釋是javap自己生成的
  • 需要知道的是,上述的檔案並非是生成的*.class檔案,*.class檔案的內容是一串接近於機器碼的十六進位制字元,開頭是一個魔數"0xCAFEBABE",該魔數是確定一個檔案是否是class檔案的標準。之後就是class編譯版本(minor version,major version),然後下邊的順序與TCV.txt的順序一樣了。
  • 在TCV.txt檔案中,多了一個無參構造器方法,該無參構造器呼叫的是TestClass的父類Object的無參構造器(即執行了super()方法),這個無參構造器是在javac變異的第三步"語義分析"的時候新增的,具體的檢視第二章 Javac編譯原理 

注意:

  • 常量池的存放內容
    • 存放所有的方法名
    • field名
    • 方法簽名(方法引數+返回值)
    • 型別名
    • class檔案中的常量值
  • 常量池的前四部分可以稱作是符號引用(即只有一些名稱,但沒有實際的地址,在執行期進行類的載入過後,會為這些東西分配實際的記憶體,到時候符號引用就會轉化為直接引用,就能被JVM用了)
  • 常量池的組成:符號引用、常量(這個常量包含我們程式碼中定義的常量,eg、字串常量,也包括class檔案中的常量,eg.SourceFile)。
  • 主版本號的對應(eg.50對應jdk6,51對應jdk7),檢視《深入理解java虛擬機器(第二版)》P167
  • Stack:運算元棧的深度(這個值就是類載入階段為運算元棧分配的深度)
  • Locals:區域性變數的分配空間(單位是slot,不是個數),對於double和long這兩個64bit的,需要兩個slot,對於其他<=32bit的,只需要一個slot
  • Args_size:方法引數的個數,包括方法引數、this(this只針對例項方法,static方法不會自動新增this)
  • inc()方法:我詳細註釋了該方法的執行過程,這也就是JVM執行一個方法的基本流程(基於棧)

提醒:

  • Code部分是我們主要關注的部分,這一部分中關鍵的部分就是每一條位元組碼指令的意義是什麼。具體的可以檢視《深入分析Java Web技術內幕(修訂版)》P124-P135

總結:

  • 掌握類檔案結構,有利於我們理解類載入機制,而瞭解了類載入機制,最直接的好處,就是我們可以自己編寫類載入工具,例如,smarty框架就是自己編寫了一個類載入器
  • 讀懂執行javap之後的位元組碼指令有利於我們理解java程式碼的執行流程,對我們定位問題也有一定的好處(雖然我在開發中還沒有用這種方式定位過問題)

相關推薦

檔案結構javap的使用

注:本文主要參考自《深入理解java虛擬機器(第二版)》 1、javap的使用與類檔案結構 使用過程: java原始碼: 1 package compile; 2 /** 3 * class位元組碼 4 */ 5 public class TestClass {

檔案結構

6.1 概述       程式碼編譯的結果是從本地機器碼轉變為位元組碼,是儲存格式發展的一小步,卻是程式語言發展的一大步。由於最近10年內虛擬機器及建立在虛擬機器之上的大量程式設計語言如雨後春筍般出現並蓬勃發展,將我們的程式編譯成二進位制本地機器碼已不再是唯一的選擇

JVM 6檔案結構

1 概述 程式碼編譯的結果從本地機器碼(Native Code)轉變為位元組碼,位元組碼與作業系統和機器指令集無關。 1.1 無關性的基石 位元組碼是構成無關性的基石。 平臺無關性 虛擬機器載入和執行同一種平臺無關性的位元組碼,從而實現程式的“一次編寫,到處執行”。

JAVA基礎-物件、抽象、介面 JAVA基礎第一-初識java JAVA基礎第二-java三大特性:封裝、繼承、多型

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java JAVA基礎第二章-java三大特

JAVA基礎-物件、抽象、介面

  前言      標題沒有看錯,真的是讓我寫個 bug!      剛接到這個需求時我內心沒有絲毫波瀾,甚至還有點激動。這可是我特長啊;終於可以光明正大的寫 bug 了

JAVA基礎-集合框架Collection篇 JAVA基礎第一-初識java JAVA基礎第二-java三大特性:封裝、繼承、多型 JAVA基礎-物件、抽象、介面 記一次list迴圈刪除元素的突發事件!

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性

JAVA基礎-集合框架Map篇 JAVA基礎第一-初識java JAVA基礎第二-java三大特性:封裝、繼承、多型 JAVA基礎-物件、抽象、介面 JAVA基礎-集合框架Collection篇

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地! 我將會持續更新java基礎知識,歡迎關注。   往期章節: JAVA基礎第一章-初識java

JVM體系結構工作方式

硬件 java內存管理 彈出 操作符 clas 基本 記錄器 pan 操作 JVM能跨計算機體系結構來執行Java字節碼,主要是由於JVM屏蔽了與各個計算機平臺的軟件和硬件之間的差異。 7.1 JVM體系結構   7.1.1 何謂JVM     模擬一個計算機來達到一個計算

JAVA-初步認識--循環結構的練習2

col 顯示 ole switch語句 認識 選擇 執行 blog spa 一. 對於循環結構while語句的練習 註解:while語句的書寫形式和if格式1的書寫手法很相似,唯一的不同點在於開頭的關鍵字不一樣。以視頻的講解來看,兩者條件語句的判斷標準是一樣的,最終輸出

if選擇結構

als mage 進入 input ima ring ext core equals if基本語法: if(條件){// 表達式 // 代碼塊 } eg: int a = 10; if(a > 1){ System.out.println("內容"); }

empty bsp image pan end include http getline 編譯 【標準類型string】 如果使用等號(=)初始化一個變量,實際執行的是拷貝初始化,編譯器把等號右側的初始化值拷貝到新創建的對象中去,與之相反,如果不使用等號,則執行的是直接初

《深入理解Java虛擬機》學習筆記( 垃圾收集器內存分配策略)

關鍵字 rem 永久 規模 是把 同時 技術 source () 第三章 垃圾收集器與內存分配策略 要解決的問題 哪些內存需要回收? 什麽時候回收? 如何回收? 概述 當需要排查各種內存溢出、內存泄漏問題時,當垃圾收集成為系統達到更高並發量的瓶頸時, 需要對內存動態分

《第一行程式碼Android》學習總結 自定義佈局控制元件

1、View是Android中最基本的元件,它可以在螢幕上繪製一塊矩形區域,並在這塊區域內響應各種事件。所有控制元件都直接或間接繼承自View。 2、ViewGroup是一種特殊的View,可以包含很多子View和子ViewGroup,是一個用於放置控制元件和佈局的容器。所有佈局都直接或間

jvm_:垃圾收集記憶體分配策略

1:回收哪些記憶體: 程式計數器,虛擬機器棧,本地方法棧屬於執行緒私有,隨執行緒而生,隨執行緒而滅,所以主要考慮方法區和堆記憶體的回收:2 2:哪些物件可以被回收:   引用計數演算法:   可達性分析演算法:GCRoots包括:                   虛擬機器棧中的引用的物件;  

八節課:存儲結構磁盤劃分

內存 分區信息 原因 一個 defaults 多少 最大 過程 其他 筆記 (借鑒請修改) 6.3、文件系統與數據資料 目前linux最常見的文件系統: ext3:日誌文件系統。宕機時可自動恢復數據資料,容量越大恢復時間越長,且不能保證百

Redis 1資料結構—字串

Key的定義注意點: 不要太長,或者太短。 統一的命名規範。 儲存String,以二進位制儲存 。最大儲存長度512M。 儲存String常用命令 賦值 取值 刪除 數值增減 擴充套件命令 連線到redis控制檯: //切換到bin目錄 cd /usr/local/bin /

岡薩雷斯:數字影象處理():灰度變換空間濾波(1)——基本灰度變換函式

一、前言 空間域指影象平面本身。這類影象處理方法直接以影象中的畫素操作為基礎。這是相對於變換域中的影象處理而言的。變換域的影象處理首先把一幅影象變換到變換域,在變換域中進行處理,然後通過反變換把處理結果返回到空間域 空間域處理主要分為灰度變換和空間濾波兩類。 灰度變換在影象的單個畫素上操

垃圾收集器記憶體分配策略

3.2物件死亡的判斷方法 3.2.1引用計數法 給物件新增一個引用計數器,每當一個地方引用它就+1,引用失效就-1,當計數器為0時就表示物件已經死亡。 缺點是無法解決迴圈引用問題 3.2.2可達性分析 將GC root作為根節點向下遍歷,無法遍歷到的物件(GC Root到這個物件不可達)就表示該物件

垃圾收集器內存分配策略

永久代 調用 標記清理 參數 通過 大小 整合 虛擬機 分析 3.2對象死亡的判斷方法 3.2.1引用計數法 給對象添加一個引用計數器,每當一個地方引用它就+1,引用失效就-1,當計數器為0時就表示對象已經死亡。 缺點是無法解決循環引用問題 3.2.2可達性分析 將GC

、選擇結構(一)

T103 周旭 一、if結構 1.使用基本的if選擇結構:    語法:if(){ } 2.使用複雜條件下的if選擇結構: 運用邏輯運算子: (1): &&  與,並且   (2):||  或