一、内存泄漏 vs 内存溢出
内存泄漏:内存泄漏是指程序中已经动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至导致系统崩溃等严重后果,内存泄漏最终 会导致内存溢出;
内存溢出:内存溢出是指没有足够的内存供申请者使用;
二、ThreadLocal内存泄漏案例
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/22 10:56
* @Description: 写一段代码导致内存泄露
* VM Options:-Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails
* 说明:内存泄露最终会导致内存溢出
*/
public class ThreadLocalOomMainApp {
public static void main(String[] args) {
// 可重入锁
Lock lock = new ReentrantLock();
// 是否调用remove()方法
boolean remove = false;
// 线程池
ExecutorService pool = new ThreadPoolExecutor(
2,
10,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(20),
Executors.defaultThreadFactory(),
// new ThreadPoolExecutor.AbortPolicy
// new ThreadPoolExecutor.CallerRunsPolicy
// new ThreadPoolExecutor.DiscardOldestPolicy
new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 1; i <= 20; i++) {
pool.execute(() -> {
try {
lock.lock();
// 为了不重复使用线程,使用map标记已经使用过的线程
Map<Long, Integer> map = new ConcurrentHashMap<>(10);
Integer num = map.putIfAbsent(Thread.currentThread().getId(), 1);
if (num == null) {
ThreadLocal<Byte[]> threadLocal = new ThreadLocal<>();
threadLocal.set(new Byte[1024 * 1024]);
if (remove) {
// 解决内存泄露的关键
threadLocal.remove();
}
// 将threadLocal置位空,利于GC回收
threadLocal = null;
// 手工触发GC
System.gc();
// 调用GC后不一定马上回收,模拟等待GC回收的时间
Thread.sleep(50);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
}
System.out.println(Thread.currentThread().getName());
}
}