CompressedOops:Java压缩参考简介
在本文中,我們將向您介紹一種稱為Compressed oops的JVM優化。 壓縮oop的概念是由32位和64位體系結構之間的差異引起的。 因此,我們將對64位體系結構進行簡短的回顧,然后再深入探討壓縮oop的主題。 最后,我們將通過一個簡單的示例看到所有這些。
本文的示例代碼非常簡單,因此我們將不使用任何IDE。 在32位計算機上,壓縮oop沒有任何意義。 同樣,在6u23之前的JDK中,默認情況下未激活它。 因此,我們假設您使用的是比6u23更新的64位JDK。 我們需要的最后一個工具是內存分析器工具。 在此示例中,我們使用了行業標準的Eclipse Memory Analyzer Tool版本1.5。
1. 32位和64位
32位與64位在2000年代初風靡一時。 盡管64位CPU在超級計算機領域并不是什么新鮮事物,但直到最近,個人計算機才將其推向主流。 從32位架構過渡到64位絕非易事,從硬件到操作系統的所有事物都必須改變。 Java通過引入64位虛擬機來擁抱這種轉變。
這種過渡的主要優勢是存儲容量。 在32位系統中,您的內存地址寬度為32位(因此稱為名稱),這意味著可尋址內存的總量為2 ^ 32或4 GB RAM。 過去,這可能是一臺個人計算機的無限內存(畢竟,那些需要超過640kB RAM的內存!),但在擁有1 GB內存的智能手機被認為是低端產品的時候,卻不是。 64位體系結構解決了此限制。 在這樣的機器中,可尋址內存的理論數量為2 ^ 64,這是一個非常大的數字。 不幸的是,這只是一個理論上的上限,在現實世界中,有很多硬件和軟件因素將我們限制在較小的內存上。 例如,Windows 7 Ultimate僅支持最大192GB。 僅將單詞用于192 GB似乎有些苛刻,但與2 ^ 64相比,它顯得蒼白。 現在您已經了解了為什么64位很重要,讓我們繼續進行下一部分,并了解壓縮oop將如何為我們提供幫助。
2.理論上的壓縮
“沒有免費的午餐之類的東西”。 64位計算機中過多的內存需要付出一定的代價。 通常,一個應用程序在64位系統上會消耗更多的內存,而在非平凡的應用程序中,這個數量是不可忽略的。 壓縮的oop通過在64位環境中使用32位類指針來幫助您保留一些內存,前提是您的堆大小不會大于32 GB。 為了更詳細地了解這一點,讓我們看看如何用Java表示對象。
Java中的對象表示
為了查看Java中對象的表示方式,我們使用一個非常簡單的示例,一個持有原始int的Integer對象。 當您編寫如下簡單的代碼行時:
Integer i = new Integer(23);編譯器為此對象分配了超過32位的堆。 在Java中,int的長度為32位,但是每個對象都有標頭。 這些標頭的大小在32位和64位以及不同的VM中有所不同。 在32位虛擬機中,每個標頭字段均為一個字或4個字節。 在64位虛擬機中,保存int的字段保留為32位,但其他字段的大小加倍為8個字節(在64bit環境中為一個字)。 實際上,故事還沒有結束。 對象是按字對齊的,這意味著在64位計算機中,它們占用的內存量必須能被64整除。對我們而言,主要關注點是類指針的大小,在Hotspot VM術語中稱為Klass。 正如您在下圖中所看到的,在64位虛擬機上,klass大小為8個字節,但是啟用了壓縮oops后,大小變為4個字節。
不同VM中Integer對象的表示形式
壓縮的oop如何實現
壓縮后的oop中的oop表示普通對象指針。 這些對象指針(如上一節所述)與計算機的本機指針大小相同。 因此,在32位和64位計算機上,oops大小分別為32位或64位。 使用壓縮的oop,我們在64位計算機上具有32位指針。
壓縮的oop背后的技巧是內存的字節尋址和字尋址之間的區別。 使用字節尋址,您可以訪問內存中的每個字節,但每個字節也需要一個唯一的地址。 在32位環境中,這會將您限制為2 ^ 32字節的內存。 在字尋址中,您仍然具有相同數量的可尋址存儲塊,但是此存儲塊是一個字而不是一個字節。 在64位計算機中,一個字為8個字節。 這為JVM提供了三個零位。 Java通過轉移這些位來利用它們,以擴展可尋址內存并實現壓縮的oop。
3.壓縮的行動
要查看壓縮的操作的效果,我們使用一個簡單的應用程序。 這是一個小的Java對象,它制作了200萬個整數的鏈表。
為了能夠查看堆條件,我們使用Eclipse Memory Analyzer Tool。 由于我們沒有使用Eclipse IDE,因此我們使用獨立的應用程序。 您可以從這里下載。
由于此示例僅使用一個類,因此我們不使用Eclipse或任何其他IDE。 使用文本編輯器并創建一個名為IntegerApplication.java的文件。 在文件中鍵入以下代碼。 請記住,文件名應與java類的名稱匹配。 您可以從本文的下載部分下載類文件,而無需手動輸入。
IntegerApplication.java
import java.util.LinkedList; import java.util.List; import java.util.Scanner;public class IntegerApplication {public static void main(String[] args) {List<Integer> intList = new LinkedList<>();for(int i=0;i<2000000;i++){Integer number = new Integer(1);intList.add(number);}Scanner scanner = new Scanner(System.in);System.out.println("application is running...");String tmp = scanner.nextLine();System.exit(0);} }打開命令提示符窗口,然后導航到該文件的目錄。 使用以下命令進行編譯。
javac IntegerApplication.java現在,您應該有一個IntegerApplication.class文件。 我們運行此文件兩次,一次啟用壓縮oop,第二次不使用壓縮oop。 在高于6u23的JVM中,默認情況下啟用壓縮操作,因此您只需要通過在命令提示符下鍵入以下命令來運行應用程序:
java IntegerApplication您可能已經注意到源代碼中的Scanner對象。 它用于使應用程序保持活動狀態,直到您鍵入某些內容并終止它為止。 如果在命令提示符下看到“應用程序正在運行...”一詞,則該啟動內存分析器了。 根據您的計算機,它可能需要一段時間才能完成初始化過程。
從文件菜單中選擇獲取堆轉儲...
Craft.io選擇窗口
您將看到過程選擇窗口。 選擇名為IntegerApplication的進程,然后單擊“完成”。
一段時間后,您將進入MAT的主屏幕。 從工具欄中選擇直方圖按鈕,如圖所示:
從工具欄中選擇直方圖
現在,您可以看到應用程序中所有對象的詳細概述。 這是在啟用壓縮oop的情況下運行的簡單應用程序的直方圖。
啟用壓縮oop的應用程序的堆轉儲。
這次,我們在沒有壓縮的情況下運行應用程序。 為了禁用壓縮的oop,我們使用-XX:-UseCompressedOops標志。 您無需再次重新編譯類,只需在命令提示符下鍵入以下命令:
java -XX:-UseCompressedOops IntegerApplication同樣,當您看到“應用程序正在運行...”文本時,將獲得與以前相同的堆轉儲。 這是應用程序在沒有壓縮的情況下運行時堆轉儲的直方圖。
禁用壓縮oop的應用程序堆轉儲
正如我們預期的那樣,堆大小增加了。 堆的大部分被兩種類型的對象占據,即鏈表節點和整數。 在壓縮oop版本中,有超過200萬個整數需要3200萬個字節,而在非壓縮oop版本中則需要4800萬個字節。 通過簡單的數學運算,我們可以看到這完全符合我們的預測。
2000000 *(128/8)= 32000000或32兆字節
2000000 *(192/8)= 48000000或48兆字節
如果您在第二個方程式中注意到我們使用了192,而在上一節中,對象大小被稱為160位。 原因是Java是按字節尋址的,因此地址與最接近的8個字節對齊(在這種情況下為192位)。
4。結論
這里提供的示例是人為設計的,但這并不意味著它在現實世界的應用程序中不成立。 與H2數據庫應用程序一起測試時,壓縮的oop將堆大小從3.6兆字節減少到了3.1兆字節。 這將寶貴堆空間的使用效率提高了近14%。 如我們所見,使用壓縮的oop并沒有什么危害,實際上,大多數情況下,您不會禁用此功能。 但是,了解編譯器技巧的細節可以幫助編寫考慮性能的代碼。
下載源代碼
這是看到壓縮的oops效果起作用的示例。
下載您可以在此處下載此示例中使用的IntegerApplication類的完整代碼: IntegerApplication
翻譯自: https://www.javacodegeeks.com/2016/05/compressedoops-introduction-compressed-references-java.html
總結
以上是生活随笔為你收集整理的CompressedOops:Java压缩参考简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (unity 安卓调试)
- 下一篇: 使用Java将项目插入DynamoDB表