Java面试题:细数ThreadLocal大坑,内存泄露本可避免
一、背景
ThreadLocal是Java中用于解决多线程共享变量导致的线程安全问题的一种机制。它为每个线程分配一个独立的变量副本,从而避免了线程间的数据竞争。这个我们从上一篇文章《Java面试题:请谈谈对ThreadLocal的理解?》中已经了解。然而,如果使用不当,ThreadLocal也可能导致内存泄露。
那什么是内存泄漏,它和内存溢出有什么区别?
内存溢出(Memory overflow):没有足够的内存提供申请者使用。
内存泄漏(Memory leak):指程序申请内存后,无法释放已申请的内存空间,内存泄漏的堆积终将导致内存溢出。
二、内存泄露案例
以下是一个可能导致ThreadLocal内存泄露的代码示例:
public class ThreadLocalMemoryTest {
// 定义一个静态的ThreadLocal变量
private static final ThreadLocal<LeakyObject> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 创建一个对象并且存储到ThreadLocal中
threadLocal.set(new LeakyObject());
// 强制进行垃圾回收,以便我们可以看到对象是否被回收
System.gc();
try {
// 等待垃圾回收器进行垃圾回收
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印MemoryLeakyObject是否被回收的信息
if (LeakyObject.isInstanceActive()) {
System.out.println("LeakyObject instance is still active!");
} else {
System.out.println("LeakyObject instance has been garbage collected.");
}
while(true) {
}
}
// 一个简单的内存泄漏示例类
private static class LeakyObject {
private static boolean instanceActive = true;
@Override
protected void finalize() throws Throwable {
super.finalize();
instanceActive = false;
System.out.println("LeakyObject finalize method has been called.");
}
public static boolean isInstanceActive() {
return instanceActive;
}
}
}
运行结果如下:
LeakyObject instance is still active!
在这个例子中,我们定义了一个LeakyObject类,它有一个静态变量instanceActive来跟踪对象实例是否仍然存在。重写的finalize方法会在对象被垃圾回收器回收时被调用,并将instanceActive设置为false。
三、代码优化与内存泄露避免
为了解决这个问题,我们需要确保在不再需要ThreadLocal中的数据时释放其占用的内存,从而避免内存泄露。在System.gc();代码执行之前,模拟清除ThreadLocal中的数据。
// 模拟线程退出,应该清除ThreadLocal中的数据
threadLocal.remove();
// 强制进行垃圾回收,以便我们可以看到对象是否被回收
System.gc();
运行结果如下:
LeakyObject finalize method has been called.
LeakyObject instance has been garbage collected.
ThreadLocalMemoryLeakTest类中的main方法模拟了ThreadLocal的使用,并在使用后调用remove方法来清除ThreadLocal中的数据,然后强制进行垃圾回收并等待一段时间,最后检查对象是否被垃圾回收器回收。
四、总结
ThreadLocal作为一种解决多线程共享变量问题的机制,在正确使用的情况下可以提供很高的性能和可靠性。然而,如果不正确使用,它也可能导致内存泄露。
通过了解并避免上述案例中的问题,我们可以更好地利用ThreadLocal来提高应用程序的性能和可靠性。在本次案例中,我们是通过ThreadLocal.remove()方法,来解决内存泄漏问题。
但是其实解决ThreadLocal内存泄漏问题的方法还有2种,需要依据不同的使用场景:
- 使用不可变对象:ThreadLocal变量存储的对象最好是不可变的,因为不可变的对象不需要频繁更新,也不会因为被多个线程同时修改而出现线程安全问题。如果要修改一个ThreadLocal变量中的对象,最好使用一个新的对象替换原有的对象,从而避免引用泄漏的问题。
- 使用弱引用:ThreadLocalMap中的弱引用可以保证ThreadLocal实例在当前线程中不再被引用时能够被GC回收,从而防止内存泄漏问题的发生。
Java面试题:细数ThreadLocal大坑,内存泄露本可避免的更多相关文章
- ThreadLocal的内存泄露(转)
ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值.如果需要隔离多个线程之间的共享冲突,可以使用T ...
- ThreadLocal的内存泄露
ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值.如果需要隔离多个线程之间的共享冲突,可以使用T ...
- Java技术专题之JVM你的内存泄露了吗?
一.从一个例子开始 关于JVM的内存泄露,让我们从下面一个例子开始吧,大家来判断一下如果按照下面这种分配方式,会不会出现内存泄露呢? import java.util.List; import jav ...
- Java有了GC同样会出现内存泄露问题
1.静态集合类像HashMap.Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放,因为他们也将一直被Vector等应用着. Static ...
- java程序——CPU过高100%及内存泄露排查
CPU过高 这类问题可以使用 top 命令观察一些,CPU 是不是都被 Java 程序占用了.比如下面这个截图: 服务器的 CPU 大多都被 Java 占用了.这正是我们之前生产上 CPU 过高的一个 ...
- 2019 Java面试题
马上又是一个金九银十的招聘旺季,小编在这里给大家整理了一套各大互联网公司面试都喜欢问的一些问题或者一些出场率很高的Java面试题,给在校招或者社招路上的你一臂之力. 首先我们需要明白一个事实,招聘的一 ...
- java面试题(目录版)
在https://www.cnblogs.com/marsitman/p/9539369.html 根据自己以往的面试经验,在该基础上做了补充和删减,均链接到相应的地址(链接失效请留言评论). 一. ...
- Java 程序的内存泄露问题分析
什么是内存泄露? 广义的Memory Leak:应用占用了内存,但是不再使用(包括不能使用)该部分内存 狭义的Memory Leak:应用分配了内存,但是不能再获取该部分内存的引用(对于Java,也不 ...
- ThreadLocal以及内存泄漏
ThreadLocal是什么 ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度.但是如果滥用Thr ...
- Java读源码之ThreadLocal
前言 JDK版本: 1.8 之前在看Thread源码时候看到这么一个属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal实现的是 ...
随机推荐
- nest.sh 脚本 发布服务
每次发布后端nest 直接执行一个脚本即可 给脚本赋值权限 chomd 777 nest.sh nest.sh 脚本 #!/bin/bash cd /root/gateway-study git pu ...
- obs 录制教程 手机录屏用 向日葵 手机投屏 能用有线用有线的连接
obs 录制教程 手机录屏用 向日葵 手机投屏 稍微有点卡 华为手机有个投屏 笔记本不支持这个 miracast 淘宝有卖 投屏设备的,搜 miracast 100多米 免费的就用向日葵就得了. 最新 ...
- vue-simple-uploader 上传组件 用js调用 打开窗口上传
为什么有这个需求 需要弹框 让用户填些数据后,再进行上传,所以不能先点击上传按钮 重点1:添加id <uploader-btn :single="true" id=" ...
- C++学习笔记之基础语法
目录 基础语法 switch和if区别 枚举定义及作用域 结构体数据耐齐--缺省对齐原则 函数重载overload与C++Name Mangling 指向函数的指针与返回指针的函数 基础语法 swit ...
- [STM32]STM32双机串口通信
[STM32]STM32双机串口通信 上一篇的通信方案在发送端高强度通信下寄了,发现是函数HAL_UART_Transmit()的锅,一个函数居然能跑0.3s左右...于是打算选用DMA收发数据,但是 ...
- Java 22正式发布,一文了解全部新特性
就在昨晚,Java 22正式发布!该版本提供了 12 项功能增强,其中包括 7 项预览功能和 1 项孵化器功能.它们涵盖了对 Java 语言.API.性能以及 JDK 中包含的工具的改进. 下面就来一 ...
- BugkuCTF-游戏过关
首先将exe文件下载下来,打开看一下: 玩游戏 n是灯的序列号,m是灯的状态 如果第n盏灯的m是1,它就开,如果不是,它就关 起初所有的灯都关上了 现在你可以输入n来改变它的状态 但是你应该注意一件事 ...
- 记录--源码视角,Vue3为什么推荐使用ref而不是reactive
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 ref 和 reactive 是 Vue3 中实现响应式数据的核心 API.ref 用于包装基本数据类型,而 reactive 用于处理对 ...
- TP6框架--EasyAdmin总结:暂时的离别和新的开始
眨眼一下,因为项目初期开发的完成,我与EasyAdmin的缘分也将迎来短暂的离别,有时候静下来,感觉时间过的好快,我从4月到现在,使用EasyAdmin进行项目开发,从一个初识别PHP的菜鸟,到一个能 ...
- 从零开始的 dbt 入门教程 (dbt cloud 自动化篇)
一.引 在前面的几篇文章中,我们从 dbt core 聊到了 dbt 项目工程化,我相信前几篇文章足够各位数据开发师从零快速入门 dbt 开发,那么到现在我们更迫切需要解决的是如何让数据更新做到定时化 ...