对ThreadLocal内存泄漏问题的简单了解

ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。其有如下特点:

  • 1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。(如从控制层,服务层,持久层)
  • 2、线程间数据隔离
  • 3、进行事务操作,用于存储线程事务信息。
  • 4、数据库连接,Session会话管理。

对ThreadLocal内存泄漏问题的简单了解_第1张图片

ThreadLocalMapThreadLocal 的一个静态内部类,里面定义了Entry 来保存数据。而且是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value

对于每个线程内部有个ThreadLocal.ThreadLocalMap 变量,存取值的时候,也是从这个容器中来获取。

//threadlocal存入数据方法
public void set(T value){
    //获取当前线程对象
    Thread t=Thread.currentThread();
    //将当前线程作为入参 得到ThreadLocalMap (类似Map)
    ThreadLocalMap map =getMap(t);
    if(map!=null){
        //map不为空写入this :ThreadLocal对象
        map.set(this,value);
    }else{
        //map为空,先创建再写入
        createMap(t,value)}
}

这样设计的好处是,如果这个变量不再被其他对象使用时,可以自动回收这个ThreadLocal对象,以此避免可能的内存泄露。

如果key设为强引用,假如JVM中存在大量线程都在运行,导致JVM中存在大量ThreadLocal对象无法被回收(内存泄漏-GC 无法清理这块内存区域)。大量内存泄漏会导致内存溢出。

Key对应ThreadLocal只要发生GC就会被回收,Key变为null,Entry中Value无法被访问,随着类似线程不断存入数据,Value是不可访问对象会导致内存泄漏最终导致OOM。

使用了线程池,可以达到“线程复用”的效果。但是归还线程之前记得清除ThreadLocalMap,要不然再取出该线程的时候,ThreadLocal变量还会存在。这就不仅仅是内存泄露的问题了,整个业务逻辑都可能会出错。

写入ThreadLocal中数据用完以后一定要立即手动清除。

要防止 ThreadLocal 内存泄漏,可以考虑以下方法:

使用完 ThreadLocal 后及时调用 remove() 方法:在不再需要使用 ThreadLocal 存储的数据时,手动调用 ThreadLocal.remove() 方法将该数据从当前线程的 ThreadLocalMap 中清除。这样可以确保 ThreadLocalMap 不会持有对对象的引用,从而帮助垃圾回收器正常回收不再需要的对象。

javaCopy code
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
// 存储数据
threadLocal.set(someData);
// 使用完毕后清除
threadLocal.remove();

使用 try-with-resources 或 try-finally 块:如果你的 ThreadLocal 变量在需要清理的资源管理上下文中使用,可以使用 try-with-resources(自动清理)或 try-finally(手动清理)块来确保及时清理。

javaCopy code
try (ThreadLocalResource resource = new ThreadLocalResource()) {
    // 使用 ThreadLocalResource
}
// 或者使用 try-finally
ThreadLocalResource resource = new ThreadLocalResource();
try {
    // 使用 ThreadLocalResource
} finally {
    resource.close(); // 在 close 方法中清理 ThreadLocal 变量
}

使用 InheritableThreadLocal:如果需要在子线程中访问父线程的 ThreadLocal 变量,并且确保在子线程中正确清理,可以考虑使用 InheritableThreadLocal。这个类允许子线程继承父线程的 ThreadLocal 变量,并在子线程完成后自动清理。

javaCopy code
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("Hello, Parent Thread");
Runnable childTask = () -> {
    String value = threadLocal.get(); // 子线程可以访问父线程的 ThreadLocal 变量
    // ...
};
Thread childThread = new Thread(childTask);
childThread.start();

通过采取这些预防措施,可以有效避免 ThreadLocal 变量的内存泄漏问题,确保不再需要的对象能够被及时回收。

你可能感兴趣的:(java,jvm,开发语言)