說說內存泄漏的常見場景和排查方案?
作者:磊哥
內存泄漏是指程序中某些對象不再被使用,但由于仍然被引用,垃圾回收器無法回收這些對象,導致內存被持續占用的問題。長期的內存泄漏會導致內存溢出問題,但內存泄漏不等于內存溢出,這點面試的時候一定要注意。
內存泄漏是指程序中某些對象不再被使用,但由于仍然被引用,垃圾回收器無法回收這些對象,導致內存被持續占用的問題。
PS:這就像桶破了個洞,水慢慢漏出,最終桶里的水會越來越少。
內存泄漏 ≠ 內存溢出
這里需要注意的內存泄漏和內存溢出是不同的,內存溢出是指程序在申請內存時,系統無法提供足夠的可用內存空間以滿足需求,導致程序崩潰或異常。這就像往一個固定容量的桶里倒水,當水超過桶的容量時就會溢出。
所以說,長期的內存泄漏最終會導致內存溢出。
內存泄漏常見場景
內存泄漏的常見場景如下:
- ThreadLocal 使用不當:線程池中未清理 ThreadLocal,示例代碼如下:
// ThreadLocal未 remove 導致泄漏
private static ThreadLocal<BigObject> threadLocal = new ThreadLocal<>();
public void processRequest() {
threadLocal.set(new BigObject());
// 使用后未調用 threadLocal.remove()
}- 未關閉資源:文件流、數據庫連接等未關閉,示例代碼如下:
// 文件流未關閉導致泄漏
public void fileLeak() throws Exception {
FileInputStream fis = new FileInputStream("largeFile.txt");
// 忘記調用 fis.close()
}- 監聽器未注銷:注冊的事件監聽器未移除,示例代碼如下:
public class EventSource {
private List<EventListener> listeners = new ArrayList<>();
public void addListener(EventListener listener) {
listeners.add(listener); // 缺少 removeListener方法
}
}- 靜態集合濫用:靜態集合長期持有對象引用,示例代碼如下:
// 靜態集合導致內存泄漏
private static List<Object> cache = new ArrayList<>();
public void addToCache(Object obj) {
cache.add(obj); // 添加后從不移除
}- 不正確的equals/hashCode:導致 HashMap 無法正確識別相同對象,示例代碼如下:
// 未重寫 equals/hashCode 導致 HashMap 內存泄漏
Map<Person, Integer> map = new HashMap<>();
for(int i=0; i<100; i++) {
map.put(new Person("jon"), 1); // 每次都被視為新對象
}內存泄漏排查
內存泄漏的排查步驟如下:
- 監控內存使用趨勢:
- 使用 jconsole 或 jvisualvm 觀察內存是否持續增長。
- 關注 Full GC 后內存是否回落。
- 堆轉儲分析:
- 使用 Eclipse MAT 分析堆轉儲文件。
- 查看 Dominator Tree 找出占用內存最多的對象。
- 檢查 Reference Chain 定位阻止回收的引用。
- 代碼審查:
- 靜態集合的使用情況。
- 資源關閉邏輯(try-with-resources)。
- 監聽器的注銷機制。
- ThreadLocal 的 remove 調用。
小結
內存泄漏是指程序中某些對象不再被使用,但由于仍然被引用,垃圾回收器無法回收這些對象,導致內存被持續占用的問題。長期的內存泄漏會導致內存溢出問題,但內存泄漏不等于內存溢出,這點面試的時候一定要注意。
責任編輯:武曉燕
來源:
磊哥和Java






























