首页 > 系统相关 >threadlocal源码详解&内存泄漏分析

threadlocal源码详解&内存泄漏分析

时间:2022-11-07 23:25:00浏览次数:47  
标签:ThreadLocal threadlocal 源码 引用 key null public 详解 内存

juc基础之Threadlocal

Threadlocal底层原理

  • get方法源码

    • public T get() {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
          return setInitialValue();
      }
    • ThreadLocalMap getMap(Thread t) {
          return t.threadLocals;
      }
  • get源码分析

    • 获取当前线程

    • 获取当前线程内部的threadLocals,即一个ThreadlocalMap

    • 以当前Threadlocal对象为key,从threadLocals中取出Entry

    • 返回Entry的value属性

  • set方法源码

    • public void set(T value) {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              map.set(this, value);
          } else {
              createMap(t, value);
          }
      }
    • ThreadLocalMap getMap(Thread t) {
          return t.threadLocals;
      }
  • set源码分析

    • 获取当前线程
    • 获取当前线程内部的threadLocals,即一个即一个ThreadlocalMap
    • 以当前Threadlocal对象为key,向threadLocals中存入value

强,软,弱,虚引用

  • 强引用

    内存不足时,jvm开始进行gc,对于强引用对象,就算出现了oom也不会对该对象进行回收,死都不会回收

    • public class demo {
          public static void main(String[] args) {
              //强引用
              Object o1 = new Object();
              //模拟内存不足
              try {
                  byte[] bytes = new byte[30*1024*1024];
              } catch (Exception e){

              }
              //系统进行垃圾回收
              System.gc();
              //强引用不会被回收
              System.out.println(o1);
          }
      }
  • 软引用

    内存充足时,不会被回收,内存不足时,就会被回收

    • -Xms5m -Xmx5m -XX:+PrintGCDetails
    • public class demo {
          public static void main(String[] args) {
              //弱引用
              WeakReference<String> stringWeakReference = new WeakReference<String>("ok");
              //系统进行垃圾回收
              System.gc();
              //弱引用不会被回收
           System.out.println(stringWeakReference);
          }
      }
  • 弱引用

    只要gc,就会被回收

    • public class demo {
          public static void main(String[] args) {
              Object o1 = new Object();
              //弱引用
              System.out.println(o1);
              WeakReference<Object> objectSoftReference = new WeakReference<>(o1);
              System.out.println(objectSoftReference);
              o1=null;
              //系统进行gc
              System.gc();
              System.out.println("---------------");
              System.out.println(o1);
              System.out.println(objectSoftReference.get());
              }
          }

内存泄漏

内存泄漏:申请了内存,但是该内存一直无法释放

补充:

内存溢出:发现申请内存不足,就会报错,内存溢出的问题

  • 为什么会产生内存泄漏问题?

    • ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收这样一来,ThreadLocalMap中就会出现keynullEntry,就没有办法访问这些keynullEntryvalue,如果当前线程再迟迟不结束的话,这些Entry永远无法回收,造成内存泄漏
  • 内存泄漏-代码实现

    • public class demo {
          public static void main(String[] args) {
              MyExecutor myExecutor = new MyExecutor();
              for (int i = 0; i < 100; i++) {
                  final int finalI = i;
                  myExecutor.execute(()->{
                      ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
                      stringThreadLocal.set(""+finalI);
                  });
              }
              myExecutor.execute(()->{
                  Thread thread = Thread.currentThread();
                  ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
                  stringThreadLocal.set("ok");
              });

          }
          }

      class MyExecutor{
          private ArrayList<Runnable> threadPool = new ArrayList<>();
          private LinkedBlockingQueue<Runnable> task =new LinkedBlockingQueue<>();

          public MyExecutor() {
              for (int i = 0; i < 2; i++) {
                  Thread thread = new Thread(() -> {
                      while (true) {
                          Runnable runnable = task.poll();
                          if (runnable != null) {
                              runnable.run();
                          }
                      }
                  });
                  thread.start();
                  threadPool.add(thread);
              }
          }
          public boolean execute(Runnable runnable){
              return task.offer(runnable);
          }
      }
  • 如何避免内存泄漏问题

    • get/set方法,会检测key=null的entry并删除

      • get

      • private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }
      • set

      • for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();

            if (k == key) {
                e.value = value;
                return;
            }

            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }
    • 使用完Threadlocal后,要remove掉

  • 为什么是弱引用,不是强引用

    • key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏
    • key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get的时候会被清除
    • 使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除

标签:ThreadLocal,threadlocal,源码,引用,key,null,public,详解,内存
From: https://www.cnblogs.com/new228666/p/16867867.html

相关文章

  • 线程池原理&源码分析&手写实现
    juc基础之线程池实现原理线程池概念线程池和数据库连接池非常类似,可以统一管理和维护线程,减少没必要的开销为什么使用使用线程池?因为频繁的开启线程或者停止线程,线程需......
  • springboot整合nacos config详解
    springboot整合nacos详解@value:读取application文件@ConfigurationProperties:读取指定文件@PropertySource:自定义文件引入@PropertySource+@Value:读取自定义文件@Pro......
  • jvm双亲委派机制详解
    双亲委派机制​ 记录一下JVM的双亲委派机制学习记录。类加载器种类​ 当我们运行某一个java类的main方法时,首先需要由java虚拟机的类加载器将我们要执行的main方法所......
  • boostrap.yml 与 application.yml 详解
    boostrap.yml与application.yml详解​ 官方解释:SpringBoot中有两种上下文,bootstrap和applicationbootstrap:应用程序的父上下文,即bootstrap加载优先于applicato......
  • 网络协议之:redis protocol 详解
    目录简介redis的高级用法Redis中的piplineRedis中的Pub/SubRESPprotocolSimpleStringsBulkStringsRESPIntegersRESPArraysRESPErrorsInlinecommands总结简介redis......
  • 对<源码级调试WDF框架>一文进行补充
    MS曾在他的Github站点上提出过​​《源码级调试WDF框架》​​的方法,文中提到通过.srcfix命令使windbg源码服务器路径指向MS的源码服务器。这样当调试到wdf框架代码时,windbg......
  • 多线程详解
    1.多线程快速入门1.1进程与线程什么是进程?CPU从硬盘中读取一段程序到内存中,该执行程序的实例就叫做进程。一个程序如果被CPU多次读取到内存中,则变成多个独立的进程......
  • 2022最全Hbuilder打包成苹果IOS-App的详解
      本文相关主要记录一下使用Hbuilder打包成苹果IOS-App的详细步骤。介绍一下个人开发者账号:再说下什么是免费的苹果开发者账号,就是你没交688年费的就是免费账号,如果......
  • Vue 3.x 的 script setup 语法糖用法详解
    由于原来vue3中的setupCompositionAPI语法太过于冗长麻烦,官方又出了这么个语法糖,非常的好用了。这里介绍一些常用的语法:一、如何开始使用?1、需要关闭vetur插件,安装Vol......
  • Linux性能调试——stress压测工具详解
    目录一.stress简介1.stress简介2.stress安装二.stress使用1.stress命令2.使用三.stress测试场景四.stress-ng简介1.stress-ng简介2.stress-ng安装3.stress-ng命令一.stres......