最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java大對象(如?List、Map)如何復(fù)用以及錯誤和正確的方法講解

 更新時間:2026年01月08日 08:42:23   作者:東方佛手  
對象復(fù)用是Java編程中一種重要的優(yōu)化技術(shù),它旨在減少對象的創(chuàng)建和銷毀次數(shù),從而降低內(nèi)存分配和垃圾回收的開銷,提高程序性能,這篇文章主要介紹了Java大對象(如?List、Map)如何復(fù)用以及錯誤和正確方法的相關(guān)資料,需要的朋友可以參考下

前言

好的,這是一個非常實際且重要的問題。大對象(如 List、Map)的不當(dāng)使用是導(dǎo)致內(nèi)存抖動、GC 頻繁甚至 OOM 的常見原因。我們來詳細拆解如何正確復(fù)用它們。

錯誤的復(fù)用方法

錯誤的方法通常表現(xiàn)為 線程不安全、內(nèi)存泄漏風(fēng)險? 或 性能低下。

1. 在方法內(nèi)部直接創(chuàng)建(最典型錯誤)

這是最常見的錯誤,雖然不算“復(fù)用”,但它是問題的根源——沒有復(fù)用。

// 錯誤示例:每次調(diào)用都創(chuàng)建新對象
public void processData() {
    List<User> userList = new ArrayList<>(); // 每次進入方法都新建
    // ... 填充 userList 并進行操作
    // 方法結(jié)束,userList 成為垃圾,等待 GC
}

危害:在循環(huán)或高頻調(diào)用的方法中,會瞬間產(chǎn)生大量短生命周期的大對象,迫使 JVM 頻繁進行 Young GC,嚴重時晉升到老年代引發(fā) Full GC。

2. 使用 static字段但缺乏清理(線程不安全 & 內(nèi)存泄漏)

試圖通過靜態(tài)變量來“復(fù)用”,但忽略了線程安全和狀態(tài)污染問題。

public class BadCache {
    // 靜態(tài)字段,所有線程共享
    private static List<String> sharedList = new ArrayList<>();

    public void addData(String data) {
        sharedList.add(data); // 線程不安全!ArrayList 在并發(fā)修改時會拋出 ConcurrentModificationException 或?qū)е聰?shù)據(jù)錯亂。
    }

    public void clearAndReuse() {
        // 如果只加不減,這個列表會無限增長,最終導(dǎo)致內(nèi)存泄漏!
        // sharedList.clear(); // 必須手動清理,但何時清理?清理時機難以把握。
    }
}

3. 使用 ThreadLocal但不調(diào)用 remove()(內(nèi)存泄漏)

ThreadLocal可以實現(xiàn)線程隔離的“偽復(fù)用”,但如果使用不當(dāng),會造成嚴重的內(nèi)存泄漏。

public class BadThreadLocalUsage {
    private static final ThreadLocal<List<Object>> threadLocalList = new ThreadLocal<>();

    public void doSomething() {
        List<Object> list = threadLocalList.get();
        if (list == null) {
            list = new ArrayList<>(1000); // 每個線程首次使用時創(chuàng)建
            threadLocalList.set(list);
        }
        // ... 使用 list
        // 錯誤:方法結(jié)束后,沒有調(diào)用 threadLocalList.remove()
        // 由于 ThreadLocalMap 的 Key 是弱引用,但 Value 是強引用,會導(dǎo)致 Value 無法被回收,造成內(nèi)存泄漏。
    }
}

4. 使用線程不安全的集合并自行加鎖(性能低下)

意識到線程安全問題,但采用了笨拙的同步方式,性能極差。

public class BadSynchronizedCache {
    private final List<Object> synchronizedList = new ArrayList<>();

    public void add(Object obj) {
        synchronized (this) { // 粗暴的同步,鎖粒度太大
            synchronizedList.add(obj);
        }
    }
    // 或者使用 Collections.synchronizedList,但其迭代時仍需手動同步,容易出錯。
    // private final List<Object> syncList = Collections.synchronizedList(new ArrayList<>());
}

正確的復(fù)用方法

正確的方法核心在于:保證線程安全、避免內(nèi)存泄漏、按需清理、提升性能。

1. 使用線程安全的對象池(推薦用于創(chuàng)建成本高的大對象)

對于創(chuàng)建成本極高的大對象(如包含大量預(yù)初始化數(shù)據(jù)的 Map),可以使用對象池模式。Apache Commons Pool? 是經(jīng)典實現(xiàn)。

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

public class ExpensiveMapFactory extends BasePooledObjectFactory<Map<String, Object>> {
    @Override
    public Map<String, Object> create() {
        // 創(chuàng)建代價高的初始化過程
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < 1000; i++) {
            map.put("key" + i, expensiveInitMethod());
        }
        return map;
    }

    @Override
    public PooledObject<Map<String, Object>> wrap(Map<String, Object> map) {
        return new DefaultPooledObject<>(map);
    }

    @Override
    public void passivateObject(PooledObject<Map<String, Object>> p) {
        // 歸還對象時,清空內(nèi)容,準備下一次使用
        p.getObject().clear();
    }
}

// 使用
GenericObjectPool<Map<String, Object>> pool = new GenericObjectPool<>(new ExpensiveMapFactory());
Map<String, Object> map = pool.borrowObject(); // 從池中獲取
try {
    // ... 使用 map
} finally {
    pool.returnObject(map); // 務(wù)必在 finally 中歸還,否則對象池失效
}

優(yōu)點:嚴格控制對象數(shù)量,避免重復(fù)創(chuàng)建的高成本。

缺點:引入第三方依賴,增加了系統(tǒng)復(fù)雜性,需合理配置池大小和超時時間。

2. 使用 ThreadLocal并嚴格遵循“用完即清理”原則(推薦用于線程內(nèi)復(fù)用)

這是最常用的正確方法之一,尤其適用于 Web 應(yīng)用(一個請求一個線程)。

public class GoodThreadLocalUsage {
    private static final ThreadLocal<List<Object>> threadLocalList = new ThreadLocal<>();

    public void doSomething() {
        List<Object> list = threadLocalList.get();
        if (list == null) {
            list = new ArrayList<>(1000); // 惰性創(chuàng)建
            threadLocalList.set(list);
        }
        try {
            // ... 使用 list
        } finally {
            // !!! 關(guān)鍵步驟:使用完畢后,立即清理,防止內(nèi)存泄漏 !!!
            list.clear(); // 清空內(nèi)容,而不是解除 ThreadLocal 綁定
            // 或者 threadLocalList.remove(); // 徹底移除,下次使用會重新創(chuàng)建。對于明確生命周期的場景(如一次請求),remove() 更安全。
        }
    }
}

最佳實踐:在 Web 框架的請求攔截器(Filter/Interceptor)的 afterCompletion方法中統(tǒng)一清理所有 ThreadLocal。

3. 使用并發(fā)集合(推薦用于多線程共享只讀或可更新狀態(tài))

如果只是需要在多個線程間共享數(shù)據(jù),并且主要是讀取,或者更新操作不沖突,使用高性能的并發(fā)集合是最佳選擇。

// 場景1:讀多寫極少,可使用 CopyOnWriteArrayList
private final List<String> readOnlyList = new CopyOnWriteArrayList<>();
// 寫入時會復(fù)制整個底層數(shù)組,所以寫性能差,讀性能極佳且無鎖。

// 場景2:通用的高并發(fā) KV 存儲,使用 ConcurrentHashMap
private final ConcurrentHashMap<String, User> userCache = new ConcurrentHashMap<>();
// 分段鎖/CAS 機制,保證了高并發(fā)下的性能。它本身就是一個“復(fù)用”的容器。

// 場景3:如果需要一個全局復(fù)用的、可更新的列表,但又不想加鎖影響性能,可考慮使用不可變對象。
// 每次更新都創(chuàng)建一個新的 List,然后原子性地替換引用(適用于更新不頻繁的場景)。
private final AtomicReference<List<String>> atomicListRef = new AtomicReference<>(Collections.unmodifiableList(new ArrayList<>()));

4. 顯式緩存與清理(適用于特定生命周期的對象)

在明確的業(yè)務(wù)周期內(nèi)復(fù)用對象,并在周期結(jié)束時統(tǒng)一清理。

public class ReportGenerator {
    // 在生成一份大型報告期間復(fù)用這個列表
    private List<ReportItem> reportItems;

    public void generateReport() {
        this.reportItems = new ArrayList<>(5000); // 在方法開始時創(chuàng)建
        try {
            // ... 填充 reportItems
            // ... 多次使用 reportItems 進行計算和渲染
        } finally {
            // 報告生成完畢,生命周期結(jié)束,可以置為 null 輔助 GC
            // 或者保留,如果下一次 generateReport 能復(fù)用其容量(但通常不建議,邏輯易混淆)
            this.reportItems = null;
        }
    }
}

總結(jié)對比

方法

適用場景

優(yōu)點

缺點

注意事項

錯誤:方法內(nèi)創(chuàng)建?

任何場景

簡單

性能極差,GC 壓力大

絕對避免在循環(huán)或高頻方法中使用

錯誤:Static 字段?

幾乎無

看似簡單

線程不安全,極易內(nèi)存泄漏

禁止使用?

錯誤:ThreadLocal 不 remove?

任何場景

確定性內(nèi)存泄漏

嚴禁不清理就結(jié)束使用

正確:對象池?

創(chuàng)建成本極高的對象

節(jié)省創(chuàng)建成本,控制總量

復(fù)雜,有額外開銷

謹慎選擇,配置得當(dāng)

正確:ThreadLocal + remove?

線程內(nèi)復(fù)用,生命周期清晰(如請求)

線程安全,無鎖,性能好

濫用易導(dǎo)致內(nèi)存泄漏

必須在 finally 塊中清理?

正確:并發(fā)集合?

多線程共享數(shù)據(jù)

API 簡單,性能經(jīng)過高度優(yōu)化

并非所有場景都適用(如需要深拷貝)

根據(jù)場景選擇 ConcurrentHashMapCopyOnWriteArrayList

正確:顯式緩存與清理?

對象生命周期與業(yè)務(wù)流程綁定

邏輯清晰,易于管理

復(fù)用范圍有限

確保在生命周期結(jié)束時清理

核心心法復(fù)用是為了效率和穩(wěn)定,但不能以犧牲線程安全和引入內(nèi)存泄漏為代價。? 在選擇方法時,始終問自己三個問題:

  • 是否線程安全?

  • 是否會內(nèi)存泄漏?

  • 性能是否符合預(yù)期?

總結(jié) 

到此這篇關(guān)于Java大對象(如 List、Map)如何復(fù)用以及錯誤和正確的方法講解的文章就介紹到這了,更多相關(guān)Java大對象List、Map復(fù)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)七牛云文件圖片上傳下載

    Java實現(xiàn)七牛云文件圖片上傳下載

    本文主要介紹了Java實現(xiàn)七牛云文件圖片上傳下載,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Java不用算數(shù)運算符來實現(xiàn)求和方法

    Java不用算數(shù)運算符來實現(xiàn)求和方法

    我們都知道,Java的運算符除了具有優(yōu)先級之外,還有一個結(jié)合性的特點。當(dāng)一個表達式中出現(xiàn)多種運算符時,執(zhí)行的先后順序不僅要遵守運算符優(yōu)先級別的規(guī)定,還要受運算符結(jié)合性的約束,以便確定是自左向右進行運算還是自右向左進行運算,但是如果不用運算符怎么求和呢
    2022-04-04
  • Java利用Apache PDFBox實現(xiàn)將PDF保存為圖片

    Java利用Apache PDFBox實現(xiàn)將PDF保存為圖片

    將 PDF 文件轉(zhuǎn)換為圖片是常見的需求之一,特別是在需要將 PDF 內(nèi)容以圖像形式展示或處理時,其中最常用的是 Apache PDFBox,下面我們就來看看具體實現(xiàn)方法吧
    2026-02-02
  • Java實現(xiàn)redis分布式鎖的三種方式

    Java實現(xiàn)redis分布式鎖的三種方式

    本文主要介紹了Java實現(xiàn)redis分布式鎖的三種方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • mybatis的動態(tài)SQL以及連接池詳解

    mybatis的動態(tài)SQL以及連接池詳解

    這篇文章主要介紹了mybatis的動態(tài)SQL以及連接池詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • SpringBoot無法識別bootstrap.yml小綠葉問題的解決辦法

    SpringBoot無法識別bootstrap.yml小綠葉問題的解決辦法

    一般單獨使用?Spring?Boot?時,bootstrap.yml?文件一般是不會生效的,也就是沒有小綠葉圖標,本文給大家介紹了SpringBoot無法識別bootstrap.yml小綠葉問題的解決辦法,文中給出了兩種解決方案,需要的朋友可以參考下
    2024-07-07
  • SpringBoot為何可以使用Jar包啟動詳解

    SpringBoot為何可以使用Jar包啟動詳解

    springboot jar包啟動腳本,適用于快速啟動,刪除,重啟,以及查看狀態(tài),下面這篇文章主要給大家介紹了關(guān)于SpringBoot為何可以使用Jar包啟動的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-03-03
  • JavaWeb監(jiān)聽器Listener實例解析

    JavaWeb監(jiān)聽器Listener實例解析

    這篇文章主要為大家詳細介紹了JavaWeb監(jiān)聽器Listener實例,針對監(jiān)聽器進行進行細致分析,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JVM內(nèi)存飆升線上問題排查方式

    JVM內(nèi)存飆升線上問題排查方式

    文章主要介紹了線上CMS服務(wù)內(nèi)存增長問題的排查過程,通過分析GC日志和堆??煺?定位問題為Nacos的NamingService對象無法回收和MySQL的CallableStatement對象增長迅速,最終通過將NamingService改為單例模式解決了內(nèi)存增長問題
    2025-03-03
  • java中javamail收發(fā)郵件實現(xiàn)方法

    java中javamail收發(fā)郵件實現(xiàn)方法

    這篇文章主要為大家詳細介紹了java中javamail收發(fā)郵件實現(xiàn)方法,實例分析了javamail的使用方法與相關(guān)注意事項,非常具有實用價值,感興趣的小伙伴們可以參考一下
    2016-02-02

最新評論

潮安县| 江永县| 赤城县| 津南区| 兰溪市| 改则县| 乌鲁木齐县| 衡水市| 北辰区| 石嘴山市| 双城市| 镇平县| 当雄县| 明溪县| 永新县| 邳州市| 信宜市| 柘荣县| 进贤县| 兰西县| 磐安县| 雅江县| 安义县| 深泽县| 云林县| 广西| 同江市| 徐闻县| 宜川县| 巴青县| 贵定县| 北碚区| 元氏县| 临城县| 边坝县| 南开区| 潮安县| 东源县| 贵港市| 钟山县| 沁水县|