聊一聊Integer的緩存機制問題

来源:https://www.cnblogs.com/coderacademy/p/18044486
-Advertisement-
Play Games

在Java編程中,Integer類作為基本類型int的包裝器,提供了對象化的操作和自動裝箱與拆箱的功能。從JDK5開始引入了一項特別的優化措施——Integer緩存機制,它對於提升程式性能和減少記憶體消耗具有重要意義。接下來我們由一段代碼去打開Integer緩存機制的秘密。 public static ...


在Java編程中,Integer類作為基本類型int的包裝器,提供了對象化的操作和自動裝箱與拆箱的功能。從JDK5開始引入了一項特別的優化措施——Integer緩存機制,它對於提升程式性能和減少記憶體消耗具有重要意義。接下來我們由一段代碼去打開Integer緩存機制的秘密。

public static void main(String[] args) {  
    Integer i1 = 100;  
    Integer i2 = 100;  
    System.out.println(i1 == i2);  
    Integer i3 = 1000;  
    Integer i4 = 1000;  
    System.out.println(i3 == i4);  
}

至於答案是什麼呢?我們接著往下看,等你看完就明白了。

當你在你的Idea中寫出這段代碼的時候,Idea就會提示你要使用equals()方法區比較大小,因為Integer是對象,對象的值比較要用equals()方法,而不是使用==,這裡我們主要是研究一下Integer的緩存機制。

Integer緩存是什麼

Java的Integer類內部實現了一個靜態緩存池,用於存儲特定範圍內的整數值對應的Integer對象。預設情況下,這個範圍是-128至127。當通過Integer.valueOf(int)方法創建一個在這個範圍內的整數對象時,並不會每次都生成新的對象實例,而是復用緩存中的現有對象。我們看一下Integer.valueOf(int)的源碼:

@HotSpotIntrinsicCandidate  
public static Integer valueOf(int i) {  
    if (i >= IntegerCache.low && i <= IntegerCache.high)  
        return IntegerCache.cache[i + (-IntegerCache.low)];  
    return new Integer(i);  
}

對於Integer.valueOf(int)方法來說,由於這個方法經常用於將基本類型int轉換為包裝器對象,所以它使用了@HotSpotIntrinsicCandidate註解,這樣HotSpot JVM可能會提供一種更為高效的內部實現來處理自動裝箱操作。而IntegerCacheInteger內部的一個靜態類,負責緩存整數對象。它在類載入時被初始化,創建並緩存範圍內的所有整數對象。我們看一下IntegerCache的源碼:

private static class IntegerCache {  
    // 緩存範圍的下限,預設為-128  
    static final int low = -128;  
    // 緩存範圍的上限,初始化時動態計算(基於系統屬性或預設值127)  
    static final int high;  
    // 存儲在緩存範圍內所有Integer對象的數組  
    static final Integer cache[];  
    // 靜態初始化塊,在類載入時執行  
    static {  
        // 初始設定high為127  
        int h = 127;  
        // 嘗試從系統屬性獲取用戶自定義的最大整數值  
        String integerCacheHighPropValue =  
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");  
        // 如果系統屬性存在並且可以轉換為int類型,則更新high值  
        if (integerCacheHighPropValue != null) {  
            try {  
                int i = parseInt(integerCacheHighPropValue);  
                // 確保high至少為127,並且不超過Integer.MAX_VALUE允許的最大數組大小  
                h = Math.max(i, 127);  
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);  
            } catch( NumberFormatException nfe) {  
            }  
        }  
  
        // 設置最終確定的high值  
        high = h;  
        // 初始化cache數組,長度等於緩存範圍內的整數數量  
        cache = new Integer[(high - low) + 1];  
        // 使用迴圈填充cache數組,創建並存儲對應的Integer對象  
        int j = low;  
        for(int k = 0; k < cache.length; k++) {  
            cache[k] = new Integer(j++);  
        }  
        // 檢查,確保緩存範圍至少包含[-128, 127]  
        // 這是Java語言規範對小整數自動裝箱共用的要求  
        assert IntegerCache.high >= 127;  
    }  
    // 私有構造器,防止外部實例化此內部類的對象  
    private IntegerCache() {}  
}

IntegerCache類在Java虛擬機啟動時創建了一個固定大小的數組,用於緩存指定範圍內所有的Integer對象。這樣在後續程式運行過程中,對於這些範圍內的整數進行裝箱操作時,可以直接從緩存中獲取已存在的對象,以提升性能並減少記憶體開銷。同時,它也提供了根據系統屬性(-Djava.lang.Integer.IntegerCache.high)來自定義緩存上限的能力,並確保滿足Java語言規範關於小整數自動裝箱共用的規定。

Integer.value(int)方法中,如果int的值在IntegerCache返回的lowhigh之內,則直接返回IntegerCache中緩存的對象,否則重新new一個新的Integer對象。

而文章開頭示例中,我們使用Interge i1 = 100的方式其實是Java的自動裝箱機制,整數字面量100是一個基本類型的int值。當賦值給一個Integer引用變數i時,編譯器會隱式地調用Integer.valueOf(int)方法將這個基本類型的int值轉換為Integer對象。

整數在編程中經常被使用,特別是在迴圈計數等場景中,通過緩存整數對象,可以大幅度減少相同整數值的對象創建,從而減小記憶體占用。

由此我們可以看出因為100在[-128, 127]之內,所以i1 == i2列印true,而1000不在[-128, 127]之內,所以i3 == i4列印false
image.png

我們嘗試使用java.lang.Integer.IntegerCache.high調整一下high為1000,然後看一下效果:
image.png
image.png
列印結果都是true。

當然這個上限不要隨意去調整,調整之前,需要仔細評估應用程式的實際需求和性能影響。儘量選擇在[-128, 127]範圍內的整數值,以充分利用Integer緩存機制。

註意事項

  • 比較: 由於緩存的存在,在-128至127之間的Integer對象在進行==運算符比較時,結果可能是true,因為它們指向的是同一個記憶體地址。而在緩存範圍之外創建的Integer對象即使值相等,也會視為不同的對象,因此使用==比較會返回false。不論是否啟用緩存,對於任何兩個Integer對象,只要其包含的整數值相同,調用equals()方法始終會返回true。所以我們在比較對象時一定要使用equals()方法。

  • 不適用於所有場景: 當使用new Integer(i)直接創建Integer對象時,不會利用緩存。

  • 不要隨意去擴展緩存的上下限

總結

Integer緩存機制是Java中的一項性能優化措施,通過緩存一定範圍內的整數對象,既能減小記憶體開銷,又能提高性能。

本文已收錄於我的個人博客:碼農Academy的博客,專註分享Java技術乾貨,包括Java基礎、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中間件、架構設計、面試題、程式員攻略等


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Maven安裝本地的jar包 如果沒配置Maven的環境變數,需要先CD到maven的安裝目錄,因為沒配置環境變數,mvn命令是無法在maven安裝目錄以外的目錄運行。 cd C:\Maven\apache-maven-3.6.3\bin 然後執行下麵命令格式如下: mvn install:inst ...
  • CMS GC的英文名為:“Mostly Concurrent Mark and Sweep Garbage Collector” (最大-併發-標記-清除-垃圾收集器)。 作用範圍: 老年代 演算法: 併發標記清除演算法。 啟用參數:-XX:+UseConMarkSweepGC預設回收線程數:(處理器核 ...
  • 依賴管理有gradle和maven,在這裡選擇比較常用和方便的Maven作為工程項目和依賴管理工具來搭建SpringCloud實戰工程。主要用到的maven管理方式是多模塊和bom依賴管理。 ...
  • pandas中的cut函數可將一維數據按照給定的區間進行分組,併為每個值分配對應的標簽。其主要功能是將連續的數值數據轉化為離散的分組數據,方便進行分析和統計。 1. 數據準備 下麵的示例中使用的數據採集自王者榮耀比賽的統計數據。數據下載地址:https://databook.top/。 導入數據: ...
  • 題目描述 如果一個國家滿足下述兩個條件之一,則認為該國是 大國 : 面積至少為 300 萬平方公裡 人口至少為 2500 萬 編寫解決方案找出大國的國家名稱、人口和麵積 按任意順序返回結果表,如下例所示 測試用例 輸入: name continent area population gdp Afgh ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹如何運用`QThread`組件實現多線程功能。 ...
  • 前言 在電商、外賣、預約服務等場景中,訂單超時自動取消是一個常見的業務需求。這一功能不僅提高了系統的自動化程度,還為用戶提供了更好的體驗。需求如下: TODO 如果用戶在生成訂單後一定時間未支付,則系統自動取消訂單。 接下來就用 SpringBoot 實現訂單超時未支付自動取消的幾種方案,並提供相應 ...
  • 在如今的商業環境中,企業信息的準確性和可信度是非常重要的。尤其是在與其他公司進行合作或者與銀行等金融機構進行業務往來時,對企業的背景和信用度有著更高的要求。那麼如何有效地驗證企業的信息是否真實可信呢?挖數據平臺的獲取企業裁判文書介面 - GetJudicialDocuments將成為你的得力助手。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...