中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java對象大小實例分析

發布時間:2022-02-28 10:28:28 來源:億速云 閱讀:118 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Java對象大小實例分析”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java對象大小實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

Java 原始數據類型

Java Primitives 的大小是眾所周知的,并且從包裝盒中提供

32 位和 64 位系統的最小內存字

32 位和 64 位內存字的最小大小分別為 8 和 16 字節。任何較小的長度都按 8 舍入。在計算過程中,我們將考慮這兩種情況。

由于內存(字節大小)結構的性質,任何內存都是 8 的倍數,如果不是系統會自動添加額外的字節(但 32/64 系統的最小大小仍然是 8 和 16 字節)

Java對象

Java 對象內部沒有字段,根據規范,它只有稱為header 的元數據。Header 包含兩部分:標記詞(Mark Word) 和 類指針(class pointer)。 


功能用途大小 32 位操作系統大小 64 位
標記詞鎖(同步)、垃圾收集器信息、哈希碼(來自本機調用)4字節8 字節
類指針塊指針,數組長度(如果對象是數組)4字節4字節
全部的
8 字節(0 字節偏移)16 字節(4 字節偏移)

Java 原始包裝器

在 Java 中,除了基元和引用(最后一個是隱藏的)之外,一切都是對象。所以所有的包裝類只是包裝相應的原始類型。所以包裝器大小一般=對象頭對象+內部原始字段大小+內存間隙。所有原始包裝器的大小如下表所示:

類型內部原始尺寸標題 32 位標題 64 位總大小 32 位總大小 64 位總大小 32 位,帶間隙總大小 64 位,帶間隙
Byte18129131616
Boolean18129131616
Int481212161616
Float481212161616
Short281210141616
Char281210141616
Long881216201624
Double881216201624

Java數組

Java數組是非常相似的對象-他們也有不同的原始和對象值。該數組包含headers、數組長度及其單元格(到基元)或對其單元格的引用(對于對象)。為了方便大家清楚明白,我們繪制一個原始整數和大整數(包裝器)的數組。

基元數組(在我們的例子中是整數)

對象數組(在我們的例子中是位整數)

因此,你可以看到原始數組和對象數組之間的主要區別——帶有引用的附加層。在這個例子中,大多數內存丟失的原因是使用一個整數包裝器,它增加了 12 個額外的字節(比原始數據多 3 倍!)。

Java類

現在我們知道如何計算 Java Object、Java Primitive 和 Java Primitive Wrapper 和 Arrays。Java 中的任何類都不過是一個混合了所有提到的組件的對象:

  • 標頭(32/64 位操作系統的 8 或 12 字節)。

  • 原始(類型字節取決于原始類型)。

  • 對象/類/數組(4 字節參考大小)。

Java字符串

Java string 它是類的一個很好的例子,所以除了 header 和 hash 之外,它還封裝了 char 數組,所以對于長度為 500 的長字符串,我們有:

String
封裝字符數組
header
8-12 字節(32/64 位操作系統)header8-12 字節(32/64 位操作系統)
hash4字節數組長度4字節
char[](參考)4字節500 個字符500 * 2 字節 = 1000 字節
字符串大小16 或 24 字節 總陣列大小16(考慮間隙)+ 1000 字節 = 1016 字節
總尺寸(16 or 24) + 1016 = 1032 or 1040 bytes (for 32 and 64 bit os)

但是我們要考慮到Java String 類有不同的實現,但一般來說,主要大小由char 數組保存。

如何以編程方式計算

使用運行時檢查大小 freeMemory

最簡單但不可靠的方法是比較內存初始化前后總內存和空閑內存的差異:

long beforeUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
Object[] myObjArray = new Object[100_000];
long afterUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();

使用 Jol 庫

最好的方法是使用Aleksey Shipilev 編寫的Jol 庫。這個解決方案會讓您驚喜地發現我們可以輕松地調查任何對象/原語/數組。為此,您需要添加下一個 Maven 依賴項:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>

并提供給 ClassLayout.parseInstance 你想要估計的任何內容:

int primitive = 3; // put here any class/object/primitive/array etc
System.out.println(VM.current().details());
System.out.println(ClassLayout.parseInstance(primitive).toPrintable());

作為輸出,你將看到:

純文本1

# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0x200021de
 12   4    int Integer.value             3
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

使用探查器

作為一種選擇,怒可以使用分析器(JProfilerVM VisualizerJConsole 等)來觀察此結構或其他結構消耗了多少內存。但是這個解決方案是關于分析內存而不是對象結構。在下一段中,我們將使用 JProfiler 來確認我們的計算是正確的。

制作數據庫緩存類并計算其大小

作為一個現實的例子,我們創建類來表示某個數據庫表中的數據,其中包含 5 列和 1.000.000 條記錄。 

public class UserCache{
    public static void main(String[] args){
        User [] cachedUsers = new User[1_000_000];
        while(true){}
    }
    private static class User{
        Long id;
        String name; //assume 36 characters long
        Integer salary;
        Double account;
        Boolean isActive;
    }
}

所以現在我們創建了 100 萬用戶,對嗎?好吧,它在 User 類中的內容并不重要——我們剛剛創建了 1M 個引用。內存使用:1M * 4 字節 = 4000 KB 或 4MB。甚至沒有開始,但支付了 4MB。

為 64 位系統分析 Java 內存

為了確認我們的計算,我們執行我們的代碼并將JProfile附加到它。作為替代方案,你可以使用任何其他分析器,例如VisualVM(它是免費的)。。

提示:當你分析應用程序時,你可以不時運行 GC 以清理未使用的對象。所以分析的結果:我們有User[]4M 記錄的參考點,大小為 4000KB。

作為下一步,我們初始化對象并將它們添加到我們的數組中(名稱是唯一的 UUID 36 長度大小):

for(int i = 0;i<1_000_000;i++){
    User tempUser = new User();
    tempUser.id = (long)i;
    tempUser.name = UUID.randomUUID().toString();
    tempUser.salary = (int)i;
    tempUser.account = (double) i;
    tempUser.isActive = Boolean.FALSE;
    cachedUsers[i] = tempUser;
}

現在讓我們分析這個應用程序并確認我們的期望。你可能會提到某些值不精確,例如,字符串的大小為 24.224 而不是 24.000,但我們計算了所有字符串,包括內部 JVM 字符串以及與Boolean.FALSE對象相關的相同內容(估計為 16 字節,但在配置文件中,它顯然Boolean.TRUE是32,因為也是JVM 內部使用)。

對于 1M 記錄,我們花費了 212MB,它只有 5 個字段,并且所有字符串長度都受 36 個字符的限制。正如你所看到的,對象非常貪婪。讓我們改進 User 對象并用原語替換所有對象(字符串除外)。

僅僅通過將字段更改為基元,我們就節省了 56MB(大約 25% 的已用內存)。但我們還通過刪除用戶和原語之間的額外引用來提高性能。 

如何減少內存消耗

讓我們列出一些簡單的方法來節省內存消耗:

壓縮的 OOP

對于 64 位系統,你可以使用壓縮的 oop 參數執行 JVM。這是一個相當大的主題,

將數據從子對象提取到父對象

如果設計允許將字段從子類移動到父類,則可能會節省一些內存

帶有原語的集合

從前面的例子中,我們看到了原語包裝器是如何浪費大量內存的。原始數組不像 Java Collection 接口那樣用戶友好。但是還有一個替代方案:TroveFastUtilsEclipse Collection 等。讓我們比較 來自Trove 庫simpleArrayList<Double>TDoubleArrayListd內存使用情況。

TDoubleArrayList arrayList = new TDoubleArrayList(1_000_000);
List<Double> doubles = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
  arrayList.add(i);
  doubles.add((double) i);
}

通常,關鍵區別隱藏在 Double Primitive Wrapper 對象中,而不是 ArrayList TDoubleArrayList 結構中。因此簡化 1M 記錄的差異:

JProfiler 證實了這一點:

因此,只需更改集合,我們就可以輕松地將消耗減少 3 倍。

讀到這里,這篇“Java對象大小實例分析”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

赤峰市| 潼南县| 平邑县| 扎赉特旗| 阿鲁科尔沁旗| 黔东| 曲靖市| 阳山县| 中阳县| 华池县| 垦利县| 博乐市| 建阳市| 湟源县| 长葛市| 易门县| 墨竹工卡县| 嘉兴市| 桂阳县| 永修县| 汝城县| 郴州市| 鱼台县| 迁安市| 温泉县| 米易县| 青州市| 三江| 盐亭县| 镇江市| 静海县| 凤山县| 锡林郭勒盟| 海淀区| 太康县| 大城县| 荥阳市| 红河县| 耿马| 常宁市| 昭通市|