导致JVM内存泄漏的ThreadLocal详解

69 阅读2分钟

保证事务不失效的前提是连接要是同一个,这样的话为了保证连接时同一个就要每个方法都需要传入connection对象,那么有没有更好的办法呢?当然有了,那就是绑定连接到当前线程,而spring就是这么做的。

描述

image.png

使用

image.png

原理

如果让你实现你会怎么做呢,有一种做法是实现一个Map,key为对应线程对象,value是副本,考虑到并发问题再加上锁。但这样会存在性能问题,如下图所示:

image.png

最好的做法是这个副本就是线程本身的一部分,除此之外每个线程的副本数也是不固定的,如下是ThreadLocal具体实现:

image.png 其中ThreadLocalMap和HashMap的不同点在于hash冲突的解决,它采用开放地址法,当发现冲突时,就按照算法找一个新的空的位置。

开放定址法

image.png

泄漏

ThreadLocal使用不当会造成内存泄漏问题。

ThreadLocal内存泄漏原因如下:

image.png 当run方法执行结束后,threadLocalLV不在引用ThreadLocal()方法,而ThreadLocal()和key又是弱引用gc时会被回收掉,而这个对应的Entry对象又是被强引用着导致无法回收,且该key已经无法再被引用,成为一个无法回收的垃圾对象。

正确使用

存的值不能是同一对象,可以重写初始化方法initialValue()。示例如下:

public class Test implements Runnable {

    public static ThreadLocal<Number> value = new ThreadLocal<Number>(){
        @Override
        protected Number initialValue() {
            return new Number(0);
        }
    };
    public static void main(String[] args) {
        for (int i = 0; i<5; i++){
            new Thread(new Test()).start();
        }
    }
    @Override
    public void run() {
        Random r = new Random();
        Number number = value.get();
        number.setNum(number.getNum() + r.nextInt(100));
        value.set(number);
        System.out.println(Thread.currentThread().getName()+"="+value.get().getNum());
    }
    private static class Number{
        private Integer num;
        public Number(Integer num){
            this.num = num;
        }

        public Integer getNum() {
            return num;
        }
        public void setNum(Integer num) {
            this.num = num;
        }
    }
}

解决方法

remove()即可

应用

  • 跨方法参数传递
  • 微服务链路追踪(traceid的传递)

问题

ThreadLocal和synchronized的区别?

image.png