Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThreadLocal 表述错误 #975

Closed
ZacBi opened this issue Oct 30, 2020 · 7 comments
Closed

ThreadLocal 表述错误 #975

ZacBi opened this issue Oct 30, 2020 · 7 comments
Labels
bug Content error

Comments

@ZacBi
Copy link

ZacBi commented Oct 30, 2020

ref: 万字详解ThreadLocal关键字

Thread类有一个类型为ThreadLocal`.`ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap
ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocalvalue为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。
每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离
ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
我们还要注意Entry, 它的key是``ThreadLocal<?> k ,继承自`WeakReference, 也就是我们常说的弱引用类型。

    static class ThreadLocalMap {


        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;


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

key本身就是ThreadLocal对象的引用, 是一个强引用, ThreadLocal并不继承自WeakReference, 而是ThreadLcoalMap.Entry继承自WeakReference.下面GC关于key的内容也需要改改.

还有能把对象的后缀加上么, 看的有点难受.

@Snailclimb
Copy link
Owner

ref: 万字详解ThreadLocal关键字

Thread类有一个类型为ThreadLocal`.`ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap
ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocalvalue为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。
每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离
ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
我们还要注意Entry, 它的key是``ThreadLocal<?> k ,继承自`WeakReference, 也就是我们常说的弱引用类型。

    static class ThreadLocalMap {


        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;


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

key本身就是ThreadLocal对象的引用, 是一个强引用, ThreadLocal并不继承自WeakReference, 而是ThreadLcoalMap.Entry继承自WeakReference.下面GC关于key的内容也需要改改.

还有能把对象的后缀加上么, 看的有点难受.

感谢👍可以提交一下PR不?

@Snailclimb Snailclimb added the bug Content error label Oct 31, 2020
@LiWenGu
Copy link
Contributor

LiWenGu commented Nov 10, 2020

你可以能理解有问题,Entry是将key作为被弱引用的对象,value作为强引用,因此key是会被回收的,但是value不会被回收。如下例子,car是强引用,但是我们不会直接使用car,而是使用被包装的的entry.car,它是弱引用,会被回收:

public static void main(String[] args) {
        // baseWeak();
        Car car = new Car(22000, "sliver");
        Entry entry = new Entry(car, new Object());
        int i = 0;
        
        new Thread(() -> {
            while (true) {
                System.gc();
            }
        }).start();

        while (true) {
            if (entry.get() != null) {
                i++;
                System.out.println("Object is alive for " + i + " loops - " + entry);
            } else {
                // Object is alive for 61441 loops - Entry@490d6c15
                // null,java.lang.Object@7d4793a8
                // Object has been collected.
                System.out.println(entry.get() + "," + entry.getValue());
                System.out.println("Object has been collected.");
                break;
            }
        }
    }

class Car {
    private double price;
    private String colour;

    public Car(double price, String colour) {
        this.price = price;
        this.colour = colour;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }
}

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

    Entry(Car k, Object v) {
        super(k);
        value = v;
    }

    public Object getValue() {
        return value;
    }
}

我的机器稳定在第6W次轮询就会出现null。

@ZacBi
Copy link
Author

ZacBi commented Nov 11, 2020

@LiWenGu 感谢回复.

key本身就是ThreadLocal对象的引用, 是一个强引用,

我的意思是, 当外部方法调用threadLocalObjRef.set()时, 必然存在一个强引用, 这个强引用就是key. key所指向的对象是一个ThreadLocal对象, 进入set()方法内部时, 这个ThreadLocal对象会被增加一个弱引用. 至于你代码中的垃圾回收造返回null值, 我是有点奇怪. 这时car的强引用应该仍然存在于本地变量表啊?

image

@LiWenGu
Copy link
Contributor

LiWenGu commented Nov 11, 2020

@ZacBi
为什么会被回收你可以参考这篇博文:https://blog.csdn.net/zmx729618/article/details/54093532

值得注意的一点 , 即使有 car 引用指向对象, 且 car 是一个strong reference, weak reference weakCar指向的对象仍然被回收了. 这是因为java的编译器在发现进入while循环之后, car 已经没有被使用了, 所以进行了优化(将其置空?)

你最后贴的图有点不懂,"强引用仍然存在于本地变量表"这句话的意思是该对象是栈上分配吗?

@ZacBi
Copy link
Author

ZacBi commented Nov 11, 2020

@LiWenGu 强引用-> Strong reference(ref: Wikipedia).

强引用仍然存在于本地变量表

这句话主要是为了避免碰到编译器优化导致本地变量表的slot被复用而导致的引用消失问题, 当然在这段snippet里明显是不可能的. 而且 garbage collection 得看本地栈帧啊, 不能章口就来啊, 那篇blog也没咋说对啊. 要不您复习复习官方的垃圾回收机制(HotSpot实现)?

GC roots:
image

@LiWenGu
Copy link
Contributor

LiWenGu commented Nov 11, 2020

您这话说的就有点火箭味十足,说话确实得要依据,但是我跟你说的也是根据我的经验结论。要证明的话专门搞JVM的人估计可以给你解释清楚,比如你可以去知乎找R大这种JVM大牛。ps:我还没战术后仰呢,你就说我章口就来。既然你觉得我是胡诌乱造没有依据,那你可以关注下这个问题:https://stackoverflow.com/questions/64783413/why-this-object-with-strong-reference-will-be-recycled#comment114542031_64783413

按目前的阶段的讨论可能是JIT的优化越过了可达性分析的判断。

@ZacBi
Copy link
Author

ZacBi commented Nov 11, 2020

@LiWenGu 感谢您的答复, 讨论是有点冲动了, 向您道歉.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Content error
Projects
None yet
Development

No branches or pull requests

3 participants