java內(nèi)存占用高案例
?? 案例1:靜態(tài)集合濫用(緩存無(wú)限增長(zhǎng))
場(chǎng)景:電商系統(tǒng)用靜態(tài) HashMap緩存用戶會(huì)話數(shù)據(jù),但未清理過(guò)期會(huì)話。
現(xiàn)象:內(nèi)存持續(xù)增長(zhǎng),頻繁 Full GC 后 Old 區(qū)內(nèi)存不釋放,最終 OOM。
代碼示例:
public class SessionManager {
private static Map<String, UserSession> sessions = new HashMap<>(); // 靜態(tài)Map未清理
public static void addSession(String id, UserSession session) {
sessions.put(id, session);
}
}原理:靜態(tài)集合生命周期與 JVM 一致,所有緩存對(duì)象無(wú)法被回收。
修復(fù)方案:
- 改用
WeakHashMap或Caffeine緩存框架,自動(dòng)淘汰過(guò)期數(shù)據(jù); - 添加定時(shí)清理線程(例:每小時(shí)移除過(guò)期會(huì)話)。
?? 案例2:未關(guān)閉資源(文件流/數(shù)據(jù)庫(kù)連接)
場(chǎng)景:高頻讀取文件時(shí)忘記關(guān)閉流,或數(shù)據(jù)庫(kù)連接未歸還連接池。
現(xiàn)象:堆內(nèi)存緩慢上升,同時(shí)系統(tǒng)句柄數(shù)耗盡(too many open files)。
代碼示例:
public void readFile() throws IOException {
FileInputStream fis = new FileInputStream("large.txt"); // 未關(guān)閉!
byte[] data = fis.readAllBytes(); // 大文件直接加載到堆內(nèi)存
}原理:未關(guān)閉的流會(huì)占用堆外內(nèi)存(如文件描述符),同時(shí) byte[]對(duì)象堆積在堆內(nèi)。
修復(fù)方案:
- 必須用
try-with-resources自動(dòng)關(guān)閉資源:
try (FileInputStream fis = new FileInputStream("large.txt")) {
// 使用資源
}- 連接池配置超時(shí)自動(dòng)回收(如
maxIdleTime)。
?? 案例3:監(jiān)聽(tīng)器未注銷(事件回調(diào)堆積)
場(chǎng)景:GUI 程序或消息系統(tǒng)中,監(jiān)聽(tīng)器注冊(cè)后未移除。
現(xiàn)象:內(nèi)存緩慢增長(zhǎng),Old 區(qū)存在大量 EventListener對(duì)象。
代碼示例:
public class NotificationService {
private List<EventListener> listeners = new ArrayList<>();
public void addListener(EventListener listener) {
listeners.add(listener); // 添加后未提供移除方法
}
}原理:監(jiān)聽(tīng)器持有業(yè)務(wù)對(duì)象引用(如用戶實(shí)例),即使業(yè)務(wù)對(duì)象已失效也無(wú)法回收。
修復(fù)方案:
- 提供
removeListener()方法并在對(duì)象銷毀時(shí)調(diào)用; - 使用
WeakReference包裝監(jiān)聽(tīng)器,避免強(qiáng)引用阻塞回收。
?? 案例4:?jiǎn)卫J匠钟型獠恳?/h2>
場(chǎng)景:?jiǎn)卫龑?duì)象引用了短生命周期對(duì)象(如 Activity)。
現(xiàn)象:Android 應(yīng)用卡頓,后臺(tái)內(nèi)存居高不下。
代碼示例:
public class AppConfig {
private static AppConfig instance;
private Context context; // 持有Activity引用
private AppConfig(Context context) {
this.context = context; // 錯(cuò)誤:Activity銷毀后單例仍持有其引用
}
public static AppConfig getInstance(Context context) {
if (instance == null) {
instance = new AppConfig(context);
}
return instance;
}
}原理:?jiǎn)卫芷?= 應(yīng)用生命周期,其持有的 Context即使失效也無(wú)法回收。
修復(fù)方案:
- 用
Application Context代替Activity Context; - 對(duì)短生命周期對(duì)象使用弱引用:
WeakReference<Context>。
?? 案例5:ThreadLocal 誤用(線程池場(chǎng)景)
場(chǎng)景:線程池任務(wù)中使用 ThreadLocal后未清理。
現(xiàn)象:線程復(fù)用導(dǎo)致 ThreadLocal數(shù)據(jù)堆積,Old 區(qū)內(nèi)存階梯式上升。
代碼示例:
private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
executor.submit(() -> {
threadLocal.set(new byte[10 * 1024 * 1024]); // 10MB大對(duì)象
// 任務(wù)結(jié)束未調(diào)用 threadLocal.remove()
});原理:線程池復(fù)用線程時(shí),ThreadLocal上次設(shè)置的值未被清除,持續(xù)占用內(nèi)存。
修復(fù)方案:
- 必須在
finally塊中清理:
try {
threadLocal.set(data);
// 業(yè)務(wù)邏輯
} finally {
threadLocal.remove(); // 強(qiáng)制清理
}- 避免
static + ThreadLocal組合。
?? 案例6:匿名內(nèi)部類隱式引用
場(chǎng)景:非靜態(tài)內(nèi)部類(如 Handler/Runnable)持有外部類引用。
現(xiàn)象:Android 頁(yè)面關(guān)閉后內(nèi)存不釋放。
代碼示例:
public class MainActivity extends Activity {
void startTask() {
new Thread(() -> {
// 匿名內(nèi)部類隱式持有MainActivity引用
System.out.println(MainActivity.this);
}).start();
}
}原理:非靜態(tài)內(nèi)部類自動(dòng)持有外部類實(shí)例,導(dǎo)致外部類無(wú)法回收。
修復(fù)方案:
- 改用 靜態(tài)內(nèi)部類 + 弱引用:
private static class MyTask implements Runnable {
private WeakReference<Activity> weakRef;
MyTask(Activity activity) {
weakRef = new WeakReference<>(activity);
}
@Override public void run() {
Activity activity = weakRef.get();
if (activity != null) { /* ... */ }
}
}?? 總結(jié):內(nèi)存升高根因速查表
場(chǎng)景 | 內(nèi)存升高特征 | 排查線索 | 修復(fù)關(guān)鍵 |
靜態(tài)集合濫用 | Old區(qū)持續(xù)增長(zhǎng),F(xiàn)ull GC無(wú)效 | MAT中HashMap$Node占比高 | 改用弱引用緩存或定時(shí)清理 |
未關(guān)閉資源 | 堆外內(nèi)存+堆內(nèi)byte[]同步增長(zhǎng) | 系統(tǒng)句柄數(shù)超標(biāo) + DirectBuffer高 | try-with-resources自動(dòng)關(guān)閉 |
監(jiān)聽(tīng)器未注銷 | 監(jiān)聽(tīng)器對(duì)象堆積在Old區(qū) | 事件源類持有大量EventListener | 顯式調(diào)用removeListener() |
單例持有外部引用 | 單例關(guān)聯(lián)對(duì)象無(wú)法回收 | 單例字段引用短生命周期對(duì)象 | 替換為WeakReference |
ThreadLocal誤用 | 線程復(fù)用導(dǎo)致數(shù)據(jù)殘留 | ThreadLocalMap中值對(duì)象堆積 | finally塊中強(qiáng)制remove() |
匿名內(nèi)部類 | 外部類無(wú)法回收 | GC Root包含內(nèi)部類引用鏈 | 靜態(tài)內(nèi)部類+弱引用包裝 |
?? 預(yù)防建議:
- 代碼層面:避免 static濫用,所有資源操作必須配套關(guān)閉邏輯;
- 工具層面:集成 Arthas實(shí)時(shí)監(jiān)控內(nèi)存,壓測(cè)后用 MAT分析堆轉(zhuǎn)儲(chǔ)。
到此這篇關(guān)于java內(nèi)存占用高案例的文章就介紹到這了,更多相關(guān)java內(nèi)存占用高內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis自動(dòng)填充時(shí)間字段示例代碼
這篇文章主要給大家介紹了關(guān)于mybatis自動(dòng)填充時(shí)間字段的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
SpringBoot構(gòu)建RESTful API的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot構(gòu)建RESTful API的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
java實(shí)現(xiàn)word文件轉(zhuǎn)html文件
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)word文件轉(zhuǎn)html文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Java高效實(shí)現(xiàn)電商產(chǎn)品排序?qū)崙?zhàn)
這篇文章主要為大家介紹了Java高效實(shí)現(xiàn)電商產(chǎn)品排序?qū)崙?zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Java JDBC API介紹與實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池流程
JDBC是指Java數(shù)據(jù)庫(kù)連接,是一種標(biāo)準(zhǔn)Java應(yīng)用編程接口( JAVA API),用來(lái)連接 Java 編程語(yǔ)言和廣泛的數(shù)據(jù)庫(kù)。從根本上來(lái)說(shuō),JDBC 是一種規(guī)范,它提供了一套完整的接口,允許便攜式訪問(wèn)到底層數(shù)據(jù)庫(kù),本篇文章我們來(lái)了解JDBC API及數(shù)據(jù)庫(kù)連接池2022-12-12
Java實(shí)現(xiàn)定時(shí)任務(wù)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)定時(shí)任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09
SpringBoot普通類獲取spring容器中bean的操作
這篇文章主要介紹了SpringBoot普通類獲取spring容器中bean的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09

