本站消息

站长简介/公众号


站长简介:高级软件工程师,曾在阿里云,每日优鲜从事全栈开发工作,利用周末时间开发出本站,欢迎关注我的微信公众号:程序员总部,程序员的家,探索程序员的人生之路!分享IT最新技术,关注行业最新动向,让你永不落伍。了解同行们的工资,生活工作中的酸甜苦辣,谋求程序员的最终出路!

  价值13000svip视频教程,java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2021-03(283)

2021-04(154)

2021-05(100)

2021-06(41)

2021-07(7)

ThreadLocal 内存泄漏分析

发布于2022-01-06 08:14     阅读(367)     评论(0)     点赞(11)     收藏(2)


1.ThreadLocal源码

//ThreadLocal.ThreadLocalMap 类

/**
 * The table, resized as necessary.
 * table.length MUST always be a power of two.
 */
private Entry[] table;

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry继承自弱引用,以保证只要发生GC就会回收掉引用对象。
这里经常有一个疑惑,回收的是Entry对象还是ThreadLocal对象。从理论上来说,Entry对象本身是强引用,使用弱引用的目的就是为了把WeakReference它所持有的泛型类型对象及时回收,所以回收的是ThreadLocal对象;
(Entry为什么使用弱引用?)
在这里插入图片描述

2.先搞清楚弱引用回收的是什么?

查看弱引用源码:

public class WeakReference<T> extends Reference<T> {
    public WeakReference(T referent) {
        super(referent);
    }
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
//直接看Reference类
public abstract class Reference<T> {
   //引用对象
    volatile T referent;
    //通过构造函数给引用对象赋值
    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = queue;
    }
    //通过native方法获取该引用对象
    @FastNative
    private final native T getReferent();
    //通过native方法自动回收引用对象
    @FastNative
    native void clearReferent();
    //提供给java调用的主动回收对象的方法
    public void clear() {
        clearReferent();
    }
}

(demo测试:)

ThreadLocal<String> a = new ThreadLocal<>();
ThreadLocal<String> b = new ThreadLocal<>();
Entry[] f = new Entry[2];
private void testgc(){
    f[0] = new Entry(a, "aaa");
    f[1] = new Entry(b, "bbb");
    for (Entry entry : f) {
        System.out.println(entry);
    }
    a = null;
    Log.d(TAG, "start gc");
    System.gc();
    Log.d(TAG, "after gc");
    for (Entry entry : f) {
        Log.d(TAG, "print entry:" + entry);
    }
    new Handler(getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "gc时机不确定,1秒后保证gc发生之后再打印:");
            for (Entry entry : f) {
                Log.d(TAG, "entry:" + entry);
            }
        }
    }, 1000);
}

    static class Entry extends WeakReference<ThreadLocal<?>> {

        Object value;

        public Entry(ThreadLocal<?> threadLocal, Object value) {
            super(threadLocal);
            this.value = value;
        }

        @Override
        public String toString() {
            return "Entry{" +
                    "value=" + value + "\n" +
                    "threadlocal=" + this.get() +
                    '}';
        }
    }
//log
MainActivity: start gc
MainActivity: after gc
MainActivity: print entry:Entry{value=aaa threadlocal=java.lang.ThreadLocal@d997cd4}
MainActivity: print entry:Entry{value=bbb threadlocal=java.lang.ThreadLocal@e5aee7d}
MainActivity: gc时机不确定,1秒后保证gc发生之后再打印:
MainActivity: entry:Entry{value=aaa threadlocal=null}
MainActivity: entry:Entry{value=bbb threadlocal=java.lang.ThreadLocal@e5aee7d}
//结果显示:gc之后,f[0]对象仍存在,而threadlocal为null,
//说明只有内部的threadlocal被回收了

进一步说明弱引用回收的就是泛型类型对象;

3. “引用” 和 “对象” 的概念,以及它们和内存泄漏的关系

**引用:**存储在方法区的变量池、运行时数据区的虚拟机栈、或堆对象中的引用句柄,引用是运行时符号,指向的是对象在堆内存中的地址;
**对象:**分配在堆内存中的实例数据;
内存泄漏是如何发生的?:通过可达性分析之后,没有GCRoots指向的对象会被回收,那么当一个对象在业务中不再使用需要释放却还有GCRoots引用指向的时候就发生了内存泄漏。

4.ThreadLocal 泄漏分析

在这里插入图片描述

(通过源码查看泄漏位置:)

/**
 * Remove the entry for key.
 */
private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) { //这里e.get()获取ThreadLocal为null
            e.clear();//清空弱引用
            expungeStaleEntry(i); //清空Enty和其中的value
            return;
        }
    }
}
private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    // expunge entry at staleSlot
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;
    //......
}

原文链接:https://blog.csdn.net/u013168615/article/details/122329872



所属网站分类: 技术文章 > 博客

作者:天天在家

链接:http://www.javaheidong.com/blog/article/372919/dcfa84079157dbea6fd7/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

11 0
收藏该文
已收藏

评论内容:(最多支持255个字符)