Java 内存泄漏?别怪 GC,这几招帮你搞定!

Java 虽然有自动垃圾回收(GC),不少开发者就觉得内存泄漏这事儿跟自己没啥关系。可别想得太美,内存泄漏在 Java 里照样会找上门!啥叫内存泄漏?就是那些用不着了的对像还被不小心拽着引用不放,GC 想收也收不走。

Java 内存泄漏的常见元凶

  1. 1. 静态集合(Map、List 之类)
    扔进静态集合里的东西没人管着不删。

  2. 2. 没关的资源(文件、数据库连接、流)

  3. 3. 没移除的监听器和回调(比如 Swing、Android 里的事件监听)

  4. 4. ThreadLocal 变量没清理

  5. 5. 类加载器泄漏(网页应用重部署时常见)

真实案例:缓存系统里的内存泄漏
场景
一个 Java 应用用静态 HashMap 缓存用户会话。时间一长,用不着的会话还占着内存,最后崩出 OutOfMemoryError。

问题代码(会漏的写法)

import java.util.HashMap;  
import java.util.Map;  

public class SessionCache {  
    private static final Map<Long, UserSession> cache = new HashMap<>();  

    public void addUserSession(Long userId, UserSession session) {  
        cache.put(userId, session); // 不手动删就永远留着!  
    }  

    public UserSession getSession(Long userId) {  
        return cache.get(userId);  
    }  

    // 缺了个清理无用会话的方法!  
}

为啥会漏?

  • • 静态 Map 跟应用活得一样长。

  • • UserSession 用不着了还赖在 cache 里不走。

  • • 时间一久,堆内存塞满,OutOfMemoryError 就来了。

解决办法 1:用 WeakHashMap(自动清理)

import java.util.Map;  
import java.util.WeakHashMap;  

public class SessionCache {  
    // WeakHashMap 让 GC 能自动干掉没用的条目  
    private static final Map<Long, UserSession> cache = new WeakHashMap<>();  

    public void addUserSession(Long userId, UserSession session) {  
        cache.put(userId, session);  
    }  

    public UserSession getSession(Long userId) {  
        return cache.get(userId);  
    }  
}

为啥管用?
WeakHashMap 用的是弱引用,只要别的地方不拽着 UserSession,GC 就能把它扫走。

解决办法 2:手动清理(适合严格控制缓存)

import java.util.HashMap;  
import java.util.Map;  

public class SessionCache {  
    private static final Map<Long, UserSession> cache = new HashMap<>();  

    public void addUserSession(Long userId, UserSession session) {  
        cache.put(userId, session);  
    }  

    public UserSession getSession(Long userId) {  
        return cache.get(userId);  
    }  

    // 用不着时手动删会话  
    public void removeSession(Long userId) {  
        cache.remove(userId);  
    }  
}

啥时候用这个?

  • • 想自己掌控啥时候清缓存。

  • • 用户退出或不活跃时,调用 removeSession()。

另一个实战例子:没关的资源
问题(文件泄漏)

public void readFile(String path) {  
    try {  
        BufferedReader reader = new BufferedReader(new FileReader(path));  
        String line = reader.readLine(); // 出异常咋办?  
        // ...处理文件...  
        // 缺了 reader.close();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}

为啥会漏?

  • • 出异常时 reader 没关,文件句柄就漏了。

修复:用 Try-With-Resources(自动关闭)

public void readFile(String path) {  
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {  
        String line = reader.readLine();  
        // ...处理文件...  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}

为啥好使?
try-with-resources 保证就算出异常也能调用 close()。

咋找出 Java 内存泄漏?

  • • VisualVM / Java Mission Control:盯着堆内存用量。

  • • Eclipse Memory Analyzer (MAT):分析堆转储。

  • • -Xmx & -Xms 参数:调小堆大小,早点暴露泄漏。

  • • 性能分析工具:YourKit、JProfiler。

避免 Java 内存泄漏的绝招

  • • 少用长寿的静态集合(或者上 WeakHashMap)。

  • • 资源(连接、流、文件)一定记得关。

  • • 监听器/回调用完就删(比如 Swing 的 ActionListener)。

  • • ThreadLocal 用完调用 remove()。

  • • 生产环境盯着堆内存用量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java干货

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值