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

一文盤(pán)點(diǎn)Java中常見(jiàn)內(nèi)存泄漏場(chǎng)景與解決方法

 更新時(shí)間:2025年12月30日 09:45:59   作者:煙沙九洲  
內(nèi)存泄漏 是指對(duì)象 已經(jīng)不再被程序使用,但因?yàn)槟承┰?nbsp;無(wú)法被垃圾回收器回收,長(zhǎng)期占用內(nèi)存,最終可能引發(fā)?OOM,本文會(huì)介紹常見(jiàn)的幾類(lèi)內(nèi)存泄漏場(chǎng)景,大家可以避免一下

今天我們來(lái)一起聊一聊有哪些情況會(huì)導(dǎo)致內(nèi)存泄漏。

什么是 內(nèi)存泄漏 呢

內(nèi)存泄漏 是指對(duì)象 已經(jīng)不再被程序使用,但因?yàn)槟承┰?無(wú)法被垃圾回收器回收,長(zhǎng)期占用內(nèi)存,最終可能引發(fā) OOM(OutOfMemoryError)。

接下來(lái)我們看一下常見(jiàn)的幾類(lèi)內(nèi)存泄漏場(chǎng)景。

1、生命周期長(zhǎng)的集合

將對(duì)象放入 靜態(tài) 或 生命周期很長(zhǎng) 的集合(如 public static List list = new ArrayList<>();),即使后面不再需要,集合仍持有其引用,導(dǎo)致無(wú)法GC。

2、未關(guān)閉的資源

連接、流等資源未調(diào)用 close() 方法關(guān)閉。這些資源不僅占用內(nèi)存,還可能占用文件句柄(操作系統(tǒng)分配的唯一標(biāo)識(shí),憑它,你才能操作文件資源)、網(wǎng)絡(luò)連接等系統(tǒng)資源。比如 數(shù)據(jù)庫(kù)連接、文件流(FileInputStream)、Socket連接 等。

public class FileTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("test.txt");
            // 讀取文件,未調(diào)用 fis.close()
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 未調(diào)用 fis.close() → fis 持有 Native 引用,無(wú)法回收
        }
    }
}

3、ThreadLocal 使用不當(dāng)

將對(duì)象存入 ThreadLocal 后,未在后續(xù)調(diào)用 remove() 清理。若線(xiàn)程來(lái)自線(xiàn)程池(會(huì)復(fù)用),其 ThreadLocalMap 中的值會(huì)一直存活。

public class ThreadLocalTest {
    private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // 線(xiàn)程池(核心線(xiàn)程長(zhǎng)期存活)
        TThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                4,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        executor.submit(() -> {
            User user = new User("李四", 30);
            userThreadLocal.set(user); // 存儲(chǔ)到 ThreadLocal

            // 業(yè)務(wù)執(zhí)行完畢,未調(diào)用 remove()
            // 核心線(xiàn)程不會(huì)銷(xiāo)毀,ThreadLocal 仍持有 user 引用
        });
    }
}

ps:未進(jìn)行 remove() ,還可能會(huì)導(dǎo)致 ThreadLocal 取值串門(mén)。

4、內(nèi)部類(lèi)與外部類(lèi)引用

非靜態(tài)內(nèi)部類(lèi)(或匿名類(lèi))會(huì) 隱式持有 外部類(lèi)的引用。如果內(nèi)部類(lèi)實(shí)例生命周期更長(zhǎng)(如被緩存或另一個(gè)線(xiàn)程引用),會(huì)阻止外部類(lèi)被回收。

public class OuterClass {
    private byte[] bigData = new byte[1024 * 1024 * 10]; // 10MB 大對(duì)象

    // 非靜態(tài)內(nèi)部類(lèi)
    class InnerClass {
        // 內(nèi)部類(lèi)隱式持有 OuterClass 引用
    }

    public InnerClass createInner() {
        return new InnerClass();
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        InnerClass inner = outer.createInner();

        // 置空外部類(lèi)引用,但 inner 仍持有 outer 引用
        outer = null;

        // 若 inner 被靜態(tài)變量/線(xiàn)程長(zhǎng)期持有 → outer 對(duì)象(含 bigData)無(wú)法回收
    }
}

5、 監(jiān)聽(tīng)器與回調(diào)

注冊(cè)了 監(jiān)聽(tīng)器回調(diào) 后,在對(duì)象不再需要時(shí) 沒(méi)有注銷(xiāo),導(dǎo)致源對(duì)象仍持有監(jiān)聽(tīng)器的引用(比如 事件監(jiān)聽(tīng)器、消息隊(duì)列的消費(fèi)者等)。

排查工具推薦

  • MAT(Memory Analyzer Tool): 分析堆 Dump 文件,定位泄漏對(duì)象、引用鏈(誰(shuí)在持有泄漏對(duì)象);
  • VisualVM: JDK 自帶工具,監(jiān)控內(nèi)存占用趨勢(shì),生成堆 Dump,簡(jiǎn)單排查泄漏。

6、靜態(tài)集合類(lèi)

? 如 ArrayList、HashMap 等等集合類(lèi)在類(lèi)中創(chuàng)建為靜態(tài)變量時(shí),那么他們的生命周期與程序是一致的,由于一些集合沒(méi)有刪除元素的特性或者并沒(méi)有對(duì)其包含的對(duì)象進(jìn)行處理,導(dǎo)致容器中對(duì)象的生命周期與容器一致,不能被釋放回收,然而對(duì)象本已經(jīng)不再使用,就造成了內(nèi)存泄漏。這樣看來(lái),基本特征為長(zhǎng)生命周期對(duì)象持有短生命周期對(duì)象的引用,盡管短生命周期對(duì)象已不再使用,但是因?yàn)殚L(zhǎng)生命周期對(duì)象的引用使其不能被GC回收。

7、變量的作用域不合理

? 如本該唯一定義在某方法的變量定義在了全局變量。一般來(lái)講,一個(gè)變量的作用范圍大于其所被使用的范圍,可能發(fā)生內(nèi)存泄漏,表現(xiàn)在存在時(shí)間大于使用時(shí)間,即使用完了但是還不能被回收,就比如下面的列子。另外,如果沒(méi)有及時(shí)的將未使用的對(duì)象置 null,也有可能導(dǎo)致內(nèi)存泄漏。

package com.liqia.common.core;

/**
 * 內(nèi)存泄漏例子
 *
 * @author chenq
 */
public class DemoMemoryLeak {

    /**
     * 消息
     */
    private String info;

    public void receiveAndSaveInfo() {
        
        // 模擬接受消息
        receiveInfo();
        
        // 模擬存儲(chǔ)消息
        saveInfo();
    }
}

? 這里的變量 info 在方法 receiveAndSaveInfo 中進(jìn)行賦值和保存,在該方法執(zhí)行完畢后本應(yīng)該被 GC 回收,但由于全局變量的生命周期是跟隨對(duì)象的,所有當(dāng)方法執(zhí)行完不能被回收,可能造成內(nèi)存泄漏。

8、內(nèi)部類(lèi)持有外部類(lèi)

? 如果一個(gè)外部類(lèi)的實(shí)例對(duì)象的方法返回了一個(gè)內(nèi)部類(lèi)的實(shí)例對(duì)象,這個(gè)內(nèi)部類(lèi)對(duì)象被長(zhǎng)期引用了,即使那個(gè)外部類(lèi)實(shí)例對(duì)象不再被使用,但由于內(nèi)部類(lèi)持有外部類(lèi)的實(shí)例對(duì)象,這個(gè)外部類(lèi)對(duì)象將不會(huì)被垃圾回收,這也會(huì)造成內(nèi)存泄露。

9、改變哈希值

? 當(dāng)一個(gè)對(duì)象被存儲(chǔ)進(jìn) HashSet 集合中以后,就不能修改這個(gè)對(duì)象中的那些參與計(jì)算哈希值的字段了,否則,對(duì)象修改后的哈希值與最初存儲(chǔ)進(jìn) HashSet 集合中時(shí)的哈希值就不同了,在這種情況下,即使在 contains 方法使用該對(duì)象的當(dāng)前引用作為的參數(shù)去 HashSet 集合中檢索對(duì)象,也將返回找不到對(duì)象的結(jié)果,這也會(huì)導(dǎo)致無(wú)法從 HashSet 集合中單獨(dú)刪除當(dāng)前對(duì)象,造成內(nèi)存泄露。

10、棧引起的內(nèi)存泄漏

? 這段模擬棧操作的代碼存在隱蔽的內(nèi)存泄漏問(wèn)題。定位到pop()函數(shù),在return語(yǔ)句中,當(dāng)我們彈出一個(gè)元素時(shí),只是簡(jiǎn)單的讓棧頂指針(size)-1。邏輯上,棧中的這個(gè)元素已經(jīng)彈出,已經(jīng)沒(méi)有用了。但是事實(shí)上,被彈出的元素依然存在于elements數(shù)組中,它依然被elements數(shù)組所引用,GC是無(wú)法回收被引用著的對(duì)象的。也許你期望等這整個(gè)棧失去引用(將被GC回收時(shí)),棧內(nèi)的elements數(shù)組一起被GC回收。但是實(shí)際的使用過(guò)程中,又有誰(shuí)能夠預(yù)料到這個(gè)棧會(huì)存活多長(zhǎng)時(shí)間。為了保險(xiǎn)起見(jiàn),我們需要在彈出一個(gè)元素的時(shí)候,就讓這個(gè)元素失去引用,便于GC回收。我們只需要讓Pop()函數(shù)彈出時(shí),同時(shí)解除對(duì)彈出元素的引用即可。

package com.liqia.common.core;

import java.util.Arrays;
import java.util.EmptyStackException;

/**
 * 內(nèi)存泄漏例子
 *
 * @author chenq
 */
public class DemoMemoryLeak {
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private Object[] elements;
    private int size = 0;

    public DemoMemoryLeak() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object o) {
        ensureCapacity();
        elements[size++] = o;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
        // Object o = elements[--size];
        // elements[size] = null;
        // return o;
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, size * 2 + 1);
        }
    }
}

11、緩存泄漏

? 內(nèi)存泄漏的另一個(gè)常見(jiàn)來(lái)源是緩存,一旦你把對(duì)象引用放入到緩存中,他就很容易遺忘,對(duì)于這個(gè)問(wèn)題,可以使用WeakHashMap代表緩存,此種Map的特點(diǎn)是,當(dāng)除了自身有對(duì)key的引用外,此key沒(méi)有其他引用那么此map會(huì)自動(dòng)丟棄此值。

這里我只列了常見(jiàn)的幾種情況,歡迎大家補(bǔ)充其他內(nèi)存泄漏場(chǎng)景。

到此這篇關(guān)于一文盤(pán)點(diǎn)Java中常見(jiàn)內(nèi)存泄漏場(chǎng)景與解決方法的文章就介紹到這了,更多相關(guān)Java內(nèi)存泄漏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring boot 與kafka集成的示例代碼

    spring boot 與kafka集成的示例代碼

    這篇文章主要介紹了spring boot 與kafka集成的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • Spring Boot集成Sorl搜索客戶(hù)端的實(shí)現(xiàn)代碼

    Spring Boot集成Sorl搜索客戶(hù)端的實(shí)現(xiàn)代碼

    本篇文章主要介紹了Spring Boot集成Sorl搜索客戶(hù)端的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • SpringBoot讀取excel表格的示例代碼

    SpringBoot讀取excel表格的示例代碼

    這篇文章主要介紹了SpringBoot讀取excel表格的示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java中進(jìn)行異步調(diào)用失敗的解決方法詳解

    Java中進(jìn)行異步調(diào)用失敗的解決方法詳解

    異步編程是一種非阻塞的編程模式,允許程序在等待某個(gè)操作完成時(shí)繼續(xù)執(zhí)行其他任務(wù),而不是一直等待,下面我們就來(lái)看看異步調(diào)用失敗的相關(guān)解決方法吧
    2026-01-01
  • SpringBoot通過(guò)RedisTemplate執(zhí)行Lua腳本的方法步驟

    SpringBoot通過(guò)RedisTemplate執(zhí)行Lua腳本的方法步驟

    這篇文章主要介紹了SpringBoot通過(guò)RedisTemplate執(zhí)行Lua腳本的方法步驟,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 解決springjpa的局部更新字段問(wèn)題

    解決springjpa的局部更新字段問(wèn)題

    這篇文章主要介紹了解決springjpa的局部更新字段問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • 生成8位隨機(jī)不重復(fù)的數(shù)字編號(hào)的方法

    生成8位隨機(jī)不重復(fù)的數(shù)字編號(hào)的方法

    生成隨機(jī)不重復(fù)的數(shù)字編號(hào)在某些情況下也會(huì)用到,本文以生成8位隨機(jī)不重復(fù)的數(shù)字編號(hào)為例與大家分享下具體的實(shí)現(xiàn)過(guò)程,感興趣的朋友可以參考下
    2013-09-09
  • SpringBoot上傳臨時(shí)文件被刪除引起報(bào)錯(cuò)的解決

    SpringBoot上傳臨時(shí)文件被刪除引起報(bào)錯(cuò)的解決

    這篇文章主要介紹了SpringBoot上傳臨時(shí)文件被刪除引起報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java+opencv3.2.0實(shí)現(xiàn)重映射

    Java+opencv3.2.0實(shí)現(xiàn)重映射

    這篇文章主要為大家詳細(xì)介紹了Java+opencv3.2.0實(shí)現(xiàn)重映射的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • java中判斷map<String,List>是否有key的四種方法

    java中判斷map<String,List>是否有key的四種方法

    這篇文章主要為大家詳細(xì)介紹了java中判斷map<String,List>是否有key的四種方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-12-12

最新評(píng)論

襄汾县| 灌阳县| 河池市| 嘉兴市| 白银市| 广南县| 龙泉市| 兰溪市| 五家渠市| 巫溪县| 伊宁市| 新干县| 廉江市| 青河县| 拉萨市| 图片| 共和县| 稷山县| 漾濞| 泽普县| 太康县| 元朗区| 棋牌| 荔浦县| 金坛市| 永修县| 论坛| 汨罗市| 神农架林区| 中超| 西昌市| 宿迁市| 平凉市| 蒙山县| 哈密市| 赞皇县| 上林县| 色达县| 吉木萨尔县| 明溪县| 钟山县|