内存溢出和内存泄漏区别
对比项 | 内存溢出(OOM) | 内存泄漏(Memory Leak) |
---|---|---|
定义 | 程序请求的内存超出了 JVM 可用内存 | 不再使用的对象仍然被引用,导致 GC 无法回收 |
本质 | 申请的内存超限,无法再分配 | 对象的生命周期比预期更长,导致无用对象占用内存 |
影响 | 直接导致程序崩溃 | 程序运行变慢,可能最终导致 OOM |
原因 | 过多对象创建、JVM 配置不足、无限递归等 | 没有释放资源(如静态集合、线程、Socket、数据库连接等) |
修复方法 | 优化代码、增加 JVM 堆大小、减少无用对象 | 及时释放资源、使用弱引用(WeakReference)、优化对象管理 |
溢出案例
现象 :不断向 堆(Heap) 中分配内存,导致 java.lang.OutOfMemoryError: Java heap space
。
案例
java
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
static class OOMObject {}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject()); // 不断向 list 添加对象,导致堆溢出
}
}
}
原因
- 不断创建新对象,导致 堆内存被占满,GC 也无法回收。
解决方案
- 代码优化,避免对象长时间引用
- 使用 WeakReference 进行缓存管理
泄漏案例
java
// Leak example
import java.util.HashMap;
import java.util.Map;
public class HashMapLeakDemo {
public static class Key {
String title;
public Key(String title) {
this.title = title;
}
}
public static void main(String[] args) {
Map<Key, Integer> map = new HashMap<>();
map.put(new Key("1"), 1);
map.put(new Key("2"), 2);
map.put(new Key("3"), 2);
Integer integer = map.get(new Key("2"));
System.out.println(integer);
}
}
📌 问题分析 :
Key
类没有 重写equals()
和hashCode()
方法。HashMap
依赖hashCode()
计算存储位置 ,new Key("2")
生成的是新对象,默认hashCode()
不是同一个,导致get(new Key("2"))
返回null
。- 解决方案 :重写
equals()
和hashCode()
方法,保证Key
对象逻辑相等时哈希值一致。
💡 修正代码:
java
public static class Key {
String title;
public Key(String title) {
this.title = title;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Key key = (Key) obj;
return title.equals(key.title);
}
@Override
public int hashCode() {
return title.hashCode();
}
}
🔹 这样 map.get(new Key("2"))
就能正确返回 2
了! 🚀
即使提供了 equals 方法和 hashCode 方法,也要非常小心,尽量避免使用自定义的对象作为 Key。