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

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

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

前言

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

錯(cuò)誤的復(fù)用方法

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

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

這是最常見(jiàn)的錯(cuò)誤,雖然不算“復(fù)用”,但它是問(wèn)題的根源——沒(méi)有復(fù)用。

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

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

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

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

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

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

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

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

ThreadLocal可以實(shí)現(xiàn)線程隔離的“偽復(fù)用”,但如果使用不當(dāng),會(huì)造成嚴(yán)重的內(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); // 每個(gè)線程首次使用時(shí)創(chuàng)建
            threadLocalList.set(list);
        }
        // ... 使用 list
        // 錯(cuò)誤:方法結(jié)束后,沒(méi)有調(diào)用 threadLocalList.remove()
        // 由于 ThreadLocalMap 的 Key 是弱引用,但 Value 是強(qiáng)引用,會(huì)導(dǎo)致 Value 無(wú)法被回收,造成內(nèi)存泄漏。
    }
}

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

意識(shí)到線程安全問(wèn)題,但采用了笨拙的同步方式,性能極差。

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

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

正確的復(fù)用方法

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

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

對(duì)于創(chuàng)建成本極高的大對(duì)象(如包含大量預(yù)初始化數(shù)據(jù)的 Map),可以使用對(duì)象池模式。Apache Commons Pool? 是經(jīng)典實(shí)現(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)建代價(jià)高的初始化過(guò)程
        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) {
        // 歸還對(duì)象時(shí),清空內(nèi)容,準(zhǔn)備下一次使用
        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 中歸還,否則對(duì)象池失效
}

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

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

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

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

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(); // 徹底移除,下次使用會(huì)重新創(chuàng)建。對(duì)于明確生命周期的場(chǎng)景(如一次請(qǐng)求),remove() 更安全。
        }
    }
}

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

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

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

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

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

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

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

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

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

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

總結(jié)對(duì)比

方法

適用場(chǎng)景

優(yōu)點(diǎn)

缺點(diǎn)

注意事項(xiàng)

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

任何場(chǎng)景

簡(jiǎn)單

性能極差,GC 壓力大

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

錯(cuò)誤:Static 字段?

幾乎無(wú)

看似簡(jiǎn)單

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

禁止使用?

錯(cuò)誤:ThreadLocal 不 remove?

任何場(chǎng)景

無(wú)

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

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

正確:對(duì)象池?

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

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

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

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

正確:ThreadLocal + remove?

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

線程安全,無(wú)鎖,性能好

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

必須在 finally 塊中清理?

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

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

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

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

根據(jù)場(chǎng)景選擇 ConcurrentHashMapCopyOnWriteArrayList

正確:顯式緩存與清理?

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

邏輯清晰,易于管理

復(fù)用范圍有限

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

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

  • 是否線程安全?

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

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

總結(jié) 

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

    SpringBoot無(wú)法識(shí)別bootstrap.yml小綠葉問(wèn)題的解決辦法

    一般單獨(dú)使用?Spring?Boot?時(shí),bootstrap.yml?文件一般是不會(huì)生效的,也就是沒(méi)有小綠葉圖標(biāo),本文給大家介紹了SpringBoot無(wú)法識(shí)別bootstrap.yml小綠葉問(wèn)題的解決辦法,文中給出了兩種解決方案,需要的朋友可以參考下
    2024-07-07
  • SpringBoot為何可以使用Jar包啟動(dòng)詳解

    SpringBoot為何可以使用Jar包啟動(dòng)詳解

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

    JavaWeb監(jiān)聽(tīng)器Listener實(shí)例解析

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

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

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

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

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

最新評(píng)論

长寿区| 新源县| 永州市| 永昌县| 铜川市| 玉屏| 津市市| 大同市| 黄冈市| 连城县| 喀喇沁旗| 磴口县| 安丘市| 镇坪县| 崇文区| 福鼎市| 红原县| 林周县| 临城县| 扎兰屯市| 桂林市| 白朗县| 黑龙江省| 青州市| 台北市| 富锦市| 峨边| 元朗区| 蓬溪县| 舟山市| 台安县| 嘉峪关市| 玉树县| 江门市| 栖霞市| 黔南| 曲麻莱县| 达州市| 仙居县| 息烽县| 崇礼县|