深入理解JVM虛擬機器:(三)類檔案結構(上)
前言
在上一篇深入理解JVM虛擬機器:(二)垃圾收集器概述 文章中,我們瞭解了Java虛擬機器中垃圾收集器的種類以及垃圾回收的方式等,這一篇,我們將去了解一下Java中類檔案的內部構造,由於這一章比較抽象,因此將會分為兩篇文章進行講解。
概述
程式碼編譯的結果是從本地機器碼轉變為位元組碼,是儲存格式發展的一小步,確是程式語言發展的一大步。我們都只帶Java是一門跨平臺的語言,其在誕生之初,就提出了一個著名的口號:“一次編寫,處處執行”,Sun公司以及其他虛擬機器提供商釋出了許多可以執行在各種不同平臺上的虛擬機器,各種不同平臺的虛擬機器與所有平臺都統一使用的程式儲存格式——位元組碼是構成平臺無關性的基石。二實現語言無關性的基礎仍然是虛擬機器與位元組碼儲存格式,Java虛擬機器不和包括Java在內的任何語言繫結,它只與“Class檔案”這種特定的二進位制檔案格式所關聯,本章,我們就來看一下“Class檔案”的組成奧祕。
Class類檔案的結構
解析Class檔案的資料結構是本章的主要內容。Class檔案是一組以8位位元組為基礎單位的二進位制流,各個資料專案嚴格按照順序緊湊地排列在Class檔案中,中間沒有新增任何的分隔符,這使得整個Class檔案中的儲存內容幾乎全部是程式執行的必要資料,沒有空隙存在。當遇到需要佔用8位位元組以上空間的資料項時,則會按照高位在前的方式分割成若干個8位位元組進行儲存。
根據Java虛擬機器規範的規定,Class檔案格式採用一種類似於C語言結構體的偽結構來儲存資料,這種偽結果中只有兩種資料型別:無符號數和表,後面的解析都要以這兩種資料型別為基礎,所以這裡要先介紹這兩個概念。
無符號數資料基本的資料型別,以u1、u2、u4、u8來分別代表1個位元組、2個位元組、4個位元組和8個位元組的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼構成字串值。
表是由多個無符號數或者其他表作為資料項構成的複合資料型別,所有表都習慣性地一“_info”結尾。表用於描述有層次關係的複合結構的資料,整個Class檔案本質上就是一張表,它由下圖的表格中的資料項構成。
無論是無符號數還是表,當需要描述同一個型別但數量不定的多個數據時,經常會使用一個前置的容量計數器加若干個連續的資料項形式,這時稱這一系列連續的某一型別的資料為某一型別的集合。
Class的結構不像XML等描述語言,由於它沒有任何分割符號,所以在上圖中的資料項,無論是順序還是輸了,甚至於資料儲存的位元組序這樣的細節,都是被嚴格限定的,哪個位元組代表什麼含義,長度是多少,先後順序如何,都不允許改變。接下來我們將一起看看這個表中各個資料項的具體含義。
魔數與Class檔案的版本
每個Class檔案的頭四個位元組稱為魔數(Magic Number),它的唯一作用是確定這個檔案是否為一個能被虛擬機器接受的Class檔案。很多檔案儲存標準中都使用魔數來進行身份識別,譬如圖片格式,如gif或者jpeg等在檔案頭中都存有魔數。使用魔數而不是副檔名來進行識別主要是基於安全方面的考慮,因為副檔名可以隨意的改動。檔案格式的制定者可以自由地選擇魔數值,只要這個魔術值還沒有被廣泛採用過同時又不會引起混淆即可。
緊接著魔數的4個位元組儲存的是Class檔案的版本號:第5和第6個位元組是次版本號(Minor Version),第7和第8個位元組是主版本號(Major Version)。Java的版本號是從45開始的,JDK1.1之後的每個JDK大版本釋出主版本號向上加1(JDK1.0~1.1使用了45.0~45.3的版本號),高版本的JDK能向下相容以前版本的Class檔案,但不能執行以後版本的Class檔案,及時檔案格式並未發生任何變化,虛擬機器也必須拒絕執行超過其版本號的Class檔案。
例如:JDK1.1能支援版本號為45.0~45.65535的Class檔案,無法執行版本號為46.0以上的Class檔案,而JDK1.2則能支援45.0~46.65535的Class檔案。現在,最新的JDK版本為1.8,可生成的Class檔案主版本號最大值為52.0。
下圖列出了從JDK1.1到JDK1.7,主流JDK版本表一起輸出的預設和可支援的Class檔案版本號。
常量池
緊接著主次版本號之後的是常量池入口,常量池可以理解為Class檔案之中的資源倉庫,它是Class檔案結構中與其他專案關聯最多的資料型別,也是佔用Class檔案空間最大的資料專案之一,同時它還是在Class檔案中第一個出現的表型別資料專案。
由於常量池中的常量數量是不固定的,所以在常量池的入口需要放置一項u2型別的資料,代表常量池容量計數器。與Java中語言習慣不一樣的是,這個容量計數是從1而不是0開始的,常量池容量為十六進位制數0x0016,即十進位制的22,這就代表常量池中有21項常量,索引值範圍為1~21.在Class檔案格式規範制定之時,設計者將第0項常量空出來是有特殊考慮的,這樣做的目的在於滿足後面某些指向常量池的索引值的資料在特定情況下需要表達“不引用任何一個常量池專案”的含義,這種情況就可以吧索引值置為0來表示。Class檔案結構中只有常量池的容量計數是從1開始,對於其他集合型別,包括介面索引集合、欄位表集合、方法表集合等的容量計數都一般習慣相同,是從0開始的。
常量池中主要存放兩大類常量:字面量和符號引用。字面量比較接近於Java語言層面的常量概念,如文字字串、宣告為final的常量值等。而符號引用則屬於編譯原理方面的概念,包括了下面三類常量:
- 類和介面的全限定名
- 欄位的名稱和描述符
- 方法的名稱和描述符
Java程式碼在進行Javac編譯的時候,並不像C和C++那樣有“連線”這一步驟,而是在虛擬機器載入Class檔案中不會儲存各個方法、欄位的最終記憶體佈局資訊,因此這些欄位、方法的符號引用不經過執行轉換的話無法得到真正的記憶體入口地址,也就無法直接被虛擬機器使用。當虛擬機器執行時,需要從常量池獲得對應的符號引用,再在類建立時或執行時解析、翻譯到具體的記憶體地址之中。
訪問標誌
在常量池結束之後,緊接著的兩個位元組代表訪問標誌,這個標誌用於識別一些類或者介面層次的訪問資訊,包括:這個Class是類還是介面;是否定義為public型別;是否定義為abstract型別;如果是類的話,是否被宣告為final等。
類索引、父類索引與介面索引集合
類索引和父類索引都是一個u2型別的資料,而介面索引集合是一組u2型別的資料的集合,Class檔案中有這三項資料來確定這個類的繼承關係。類索引用於確定這個類的全限定名,父類索引用於確定這個類的父類的全限定名。由於Java語言不允許多重繼承,所以父類索引只有一個,除了java.lang.Object之外,所有的Java類都有父類,因此除了java.lang.Object外,所有Java類的父類索引都不為0.介面索引集合就用來描述這個類實現了哪些介面,這些被實現的介面將按implements語句(如果這個類本身是一個介面,則應當是extends語句)後的介面順序從左到右排列在介面索引集合中。
類索引、父類索引和介面索引集合都按順序排列在訪問標誌之後,類索引和父類索引用兩個u2型別的索引值表示,它們各自指向一個型別為CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info型別的常量中的索引值可以找到定義在CONSTANT_Utf8_info型別的常量中的全限定名字串,下圖演示了類索引查詢的過程。
對於介面索引集合,入口的第一項——u2型別的資料為介面計數器,表示索引表的容量。如果該類沒有實現任何介面,則該計數器值為0,後面介面的索引表不再佔用任何位元組。
更多Java乾貨文章請關注我的個人微信公眾號:老宣與你聊Java
相關推薦
深入理解JVM虛擬機器:(三)類檔案結構(上)
前言 在上一篇深入理解JVM虛擬機器:(二)垃圾收集器概述 文章中,我們瞭解了Java虛擬機器中垃圾收集器的種類以及垃圾回收的方式等,這一篇,我們將去了解一下Java中類檔案的內部構造,由於這一章比較抽象,因此將會分為兩篇文章進行講解。 概述 程式碼編譯的
深入理解JVM虛擬機器(三):虛擬機器效能監控工具
本部落格將講解Java虛擬機器效能監控工具的使用以及對Java虛擬機器進行效能監控的實驗。Java開發人員需要對虛擬機器效能監控工具的使用進行掌握,這是很有必要的。 1.概述 給一個系統定位問題的時候,知識、經驗是關鍵基礎,資料是依據。工具是運用知識處理資料的手段。這裡說的資料包括:
深入理解JVM虛擬機:(一)Java運行時數據區域
字面量 符號 地方 64位 因此 lower 優化 java堆大小 工作 概述 JVM是Java語言的精髓所在,因為它Java語言實現了跨平臺運行,以及自動內存管理機制等,本文將從概念上介紹JVM內存的各個區域,說明個區域的作用。 JVM運行時數據區模型 Java虛擬機在執
《深入理解JVM虛擬機器》pdf版電子書+30個總結JVM虛擬機器的技術文排版好(收藏版)
技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的
深入理解JVM虛擬機器(五):位元組碼指令簡介
Java 虛擬機器的指令由一個位元組長度的、代表著某種特定操作含義的數字(稱為操作碼)以及跟隨其後的零至多個代表此操作所需引數(運算元)而構成。由於 Java 虛擬機器採用面向運算元棧而不是暫存器的架構,所以大多數的指令都不包含運算元,只有一個操作碼。 1. 位元組碼與資料型別
深入理解JVM虛擬機器(四):Class類檔案結構(二)
屬性表在前面的講解中出現多次,在Class檔案、欄位表、方法表都可以攜帶自己的屬性表集合,用於描敘某些場景專有的資訊。為了正確解析Class檔案,《Java虛擬機器規範(第二版)》中預定義了9項虛擬機器實現應當識別的屬性。然而在最新的《Java虛擬機器規範(Java SE7)》中屬性表已經增
深入理解JVM虛擬機器(二):垃圾回收機制
談起GC,應該是讓Java程式設計師最激動的一項技術,我相信每個Java程式設計師都有探究GC本質的衝動!JVM垃圾回收機制對於瞭解物件的建立和物件的回收極為重要,是每個Java程式設計師必須掌握的技能。 本部落格圍繞三個問題來展開 哪些記憶體需要回收? 什
《深入理解JVM虛擬機器》pdf版電子書附下載連結+30個總結JVM虛擬機器的技術文排版好(收藏版)
技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的
深入理解JVM虛擬機器(九):執行期優化與JIT編譯器
1. JIT編譯器的引入 首先我們這篇文章中所說的編譯器都是指JVM的組成部分之一—即時編譯器(JIT),與生成Java位元組碼的javac編譯器要區分開來。首先我們這篇文章中所說的編譯器都是指JVM的組成部分之一—即時編譯器(JIT),與生成Java位元組碼的javac編譯器要區分開來
深入理解JVM虛擬機器(八):編譯器優化
本部落格從編譯期原始碼實現的層次上讓我們瞭解了Java原始碼編譯為位元組碼的過程,分析了Java語言中泛型、主動裝箱/拆箱、條件編譯等多種語法糖的前因後果。 1. 概述 java語言的“編譯期”其實是一段“不確定”的操作過程,因為它可能是指一個前端編譯器(其實叫“編譯器的前端”更準確
深入理解JVM虛擬機器(七):虛擬機器位元組碼執行引擎
程式碼編譯的結果就是從本地機器碼轉變為位元組碼。我們都知道,編譯器將Java原始碼轉換成位元組碼?那麼位元組碼是如何被執行的呢?這就涉及到了JVM位元組碼執行引擎,執行引擎負責具體的程式碼呼叫及執行過程。就目前而言,所有的執行引擎的基本一致: 輸入:位元組碼檔案
深入理解JVM虛擬機器1:JVM記憶體的結構與永久代的消失
所有的Java開發人員可能會遇到這樣的困惑?我該為堆記憶體設定多大空間呢?OutOfMemoryError的異常到底涉及到執行時資料的哪塊區域?該怎麼解決呢?其實如果你經常解決伺服器效能問題,那麼這些問題就會變的非常常見,瞭解JVM記憶體也是為了伺服器出現效能問題的時候可
深入理解JVM虛擬機器2:JVM垃圾回收基本原理和演算法
JVM GC基本原理與GC演算法 Java的記憶體分配與回收全部由JVM垃圾回收程序自動完成。與C語言不同,Java開發者不需要自己編寫程式碼實現垃圾回收。這是Java深受大家歡迎的眾多特性之一,能夠幫助程式設計師更好地編寫Java程式。 下面四篇教程是瞭解Jav
進入JVM的世界:《深入理解JVM虛擬機器》-- 思維導圖
進入JVM的世界:《深入理解JVM虛擬機器》-- 思維導圖 在工作的時候,其實很少會需要使用到JVM的時候,因而一直都是零零散散的看了些JVM的知識。於是便抽空看了一下這本神書,閱罷,醍醐灌頂、豁然開朗。真正的是知其然,更知其所以然。當然,看完了書,知識還不是自己的,只有留在自己的腦袋裡面的,
深入理解JVM虛擬機器4:Java class介紹與解析實踐
深入理解JVM虛擬機器4:Java class介紹與解析實踐 轉自https://juejin.im/post/589834a20ce4630056097a56 前言 身為一個java程式設計師,怎麼能不瞭解JVM呢,倘若想學習JVM,那就又必須要了解Class檔案,Class之
深入理解JVM虛擬機器3:垃圾回收器詳解
深入理解JVM虛擬機器3:垃圾回收器詳解 轉自How 2 Play Life 垃圾回收器詳解 關於 JVM 記憶體管理或者說垃圾收集,大家可能看過很多的文章了,筆者準備給大家總結下。這算是系列的第一篇,接下來一段時間會持續更新。 本文主要是翻譯《Memory Management i
深入理解Java虛擬機器:JVM高階特性與最佳實踐pdf
下載地址:網盤下載目錄:前言第一部分 走近Java第1章 走近Java1.1 概述1.2 Java技術體系1.3 Java發展史1.4 Java虛擬機發展史1.4.1 Sun Classic Exact VM1.4.2 Sun HotSpot VM1.4.3 Sun Mobi
深入理解JVM虛擬機器10:JVM常用引數以及調優實踐
本文出自我的公眾號:程式設計師江湖。 滿滿乾貨,關注就送。 轉自http://www.rowkey.me/blog/2016/11/02/java-profile/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_s
《深入理解JVM虛擬機器》讀書筆記(一)
此書一共分為5個部分:走近Java自動記憶體管理機制虛擬機器執行子系統程式編譯與程式碼優化高效併發這裡,我直接跳過第一部分的內容,開始第二部分的內容。此部分包括:Java記憶體區域與記憶體溢位異常垃圾收集器與記憶體分配策略虛擬機器效能監控與故障處理工具調優案例分析與實戰開記!
深入理解JVM虛擬機器開篇:JVM介紹與知識脈絡梳理
微信公眾號【Java技術江湖】一位阿里 Java 工程師的技術小站。作者黃小斜,專注 Java 相關技術:SSM、SpringBoot、MySQL、分散式、中介軟體、叢集、Linux、網路、多執行緒,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!(關注公眾號後回覆”J