根據這篇文章http://slurp.doc.ic.ac.uk/pubs/observing/linking.html#assignment:Java字節碼:局部變量的類型?
由於Java代碼和字節 (字節碼不包含類型的局部變量)之間的信息差異,驗證 並不需要檢查亞型的分配來局部變量,或 參數。
我的問題:爲什麼字節碼不包含局部變量的類型信息,而它確實包含參數和返回值的類型信息?
根據這篇文章http://slurp.doc.ic.ac.uk/pubs/observing/linking.html#assignment:Java字節碼:局部變量的類型?
由於Java代碼和字節 (字節碼不包含類型的局部變量)之間的信息差異,驗證 並不需要檢查亞型的分配來局部變量,或 參數。
我的問題:爲什麼字節碼不包含局部變量的類型信息,而它確實包含參數和返回值的類型信息?
首先,有幾種不同的類型概念。有編譯時類型,其中包括泛型。但是,編譯時間後,泛型不存在。
有一個變量的驗證推斷靜態類型,可以是int,float,long,double,returnaddress或對象引用。對象引用還有一個上限,因此所有引用都是例如java/lang/String
的子類型。字段還可以具有其中一種短字符類型:字節,短字符,字符或布爾值。爲了執行目的,這些處理方式與整體處理方式相同,但存儲空間不同。
最後,存在運行時類型,它與驗證的靜態類型相同,但在對象引用的情況下,表示正在引用的實例的實際類型。請注意,由於驗證者的懶惰,在某些情況下,運行時類型實際上可能不是已驗證類型的子類型。例如,聲明類型爲Comparable
的變量實際上可以在Hotspot中保存任何對象,因爲虛擬機在驗證時不會檢查接口。
編譯時間信息不會保留,除非通過反射和調試的可選屬性。這是因爲沒有理由保留它。
局部變量沒有顯式的類型信息(除了新的StackMapTable屬性,但這是一個技術性)。相反,當類加載時,字節碼驗證器通過運行靜態數據流分析來推斷每個值的類型。這樣做的目的不是爲了趕上像編譯時類型檢查這樣的錯誤,因爲它假定字節碼已經在編譯時經過了這種檢查。
相反,驗證的目的是確保指令對VM本身沒有危險。例如,它需要確保您沒有取整數並將其作爲對象引用進行匹配,因爲這可能會導致任意內存訪問和黑客入侵VM。
所以雖然字節碼值沒有明確的類型信息,但它們有一個隱式類型,它是靜態類型推斷的結果。這些細節取決於每個虛擬機的內部實現細節,儘管它們是假定遵循JVM標準。但是你只需要擔心手寫字節碼。
由於VM需要知道存儲哪種類型的數據,因此字段具有明確的類型。方法參數和返回類型在所謂的方法描述符中編碼,也用於類型檢查。他們不可能自動推斷,因爲這些值可以來自或去任何地方,而類型檢查是在每個班的基礎上完成的。
P.S.談到驗證類型時,我忽略了一些細節。對象類型還追蹤它們是否已被初始化,以及在未初始化的情況下由哪個指令創建它們。地址類型跟蹤創建它們的jsr的目標。
值得注意的是,'returnAddress'類型只用於在Java 6中被禁止的'jsr'和'jsr_w'操作碼。由現代字節碼操縱的值不會包含'returnAddress'。 –
@Tom它們不能用於大於49的主要版本的類文件中,但仍可以在現代VM上運行版本號較低的類文件。如果您願意,可以將版本設置爲低於45.0,並且這樣做會在Hotspot中啓用一些有趣的未記錄行爲。如果您只考慮Java編譯的字節碼,那麼javac生成的字節碼將永遠不會執行相當多的事情。但它仍然對字節碼黑客感興趣。 – Antimony
真的!我們必須避免這種錯誤,這有點令人遺憾,因爲它解釋了字節碼如何工作更復雜。 –
的Java bytecode
保留鍵入有關fields
信息, method
returns
和parameters
但事實並非如此, 你問, 包含local variables
類型信息。
在Java類文件中的 類型的信息呈現的bytecode
編譯比 machine code
編譯容易的任務 。因此,反編譯Java字節碼需要對大多數局部變量類型進行分析,基於堆棧的平臺化指令和結構化的loops
和conditionals
。 然而,字節碼反編譯的任務比編譯難得多。你會看到經常反編譯器 不能完全執行其預定功能
這是一個非常古老的論文。當前類文件做包括本地和堆棧變量的類型。類型不存儲在方法字節碼中,但存儲在方法附帶的StackMapTable
attribute中。
通過數據流分析重構所有局部變量和堆棧元素的類型(並且始終如此)是可能的,而不需要StackMapTable
,但是這在計算上是昂貴的。使用StackMapTable
代碼可以更快地驗證。雖然我不得不承認,我沒有看到如何驗證StackMapTable
可以比分析更快,但我對此幾乎一無所知。
我懷疑在編譯時檢查了類型值,導致代碼不再需要類型值信息,只有二進制數據。然而,方法參數&返回值在'run-time'處被調用,並且需要類型檢查信息 –
謝謝,這很有道理! – JB2
因爲就是這樣。 JDK驗證程序使用數據流分析來確定每個數據使用的「達到」類型。參數和實例/靜態變量需要聲明類型以饋入數據流。計算結果的類型OTOH可以由規則確定。除此之外,編譯時局部變量通常會消失爲堆棧值。 –