什么是内存泄漏?
内存泄漏是指在程序运行过程中,已经不再使用的内存却没有被及时释放或回收,导致系统中的可用内存逐渐减少,最终可能导致系统性能下降,甚至引发系统崩溃。内存泄漏通常发生在程序中某些对象持续占用内存空间但又无法被正确释放的情况下。
产生内存泄漏的场景
未关闭的资源
FileInputStream fis = null;
try {
fis = new FileInputStream("example.txt");
// 读取文件内容
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
// 未在finally块中关闭文件流
// 应该在此处调用fis.close()以释放资源
}
未正确释放对象引用
private static List<Object> list = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
Object obj = new Object();
list.add(obj); // 每次循环创建新对象并添加到list中
}
// 此处未调用list.clear()或将list置为null,导致list中的对象无法被释放
}
线程池导致的内存泄漏
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
executor.execute(() -> {
// 执行任务
});
}
// 程序结束后未调用executor.shutdown(),线程池资源未被释放
}
监听器和回调函数未取消注册
private static List<ActionListener> listeners = new ArrayList<>();
public static void main(String[] args) {
ActionListener listener = e -> {
// 处理事件
};
listeners.add(listener); // 添加监听器
// 程序运行结束后未取消注册监听器,导致listener对象无法被释放
}
循环引用
两个或多个对象之间相互引用,导致彼此无法被回收。
class A {
private B b;
public A() {
this.b = new B();
}
}
class B {
private A a;
public B() {
this.a = new A();
}
}
内存泄漏工具无法检测的场景
有些情况下,内存泄漏工具无法检测到内存泄漏,例如:
1)使用匿名对象。
2)使用内部类或匿名类。
避免内存泄漏的建议
1)使用合理的变量作用域。
2)避免使用全局变量。
3)及时清理不使用的对象。
4)使用 try-with-resources 语句来关闭资源。
5)避免循环引用。
6)使用内存泄漏工具来检测和解决内存泄漏问题。