H5W3
当前位置:H5W3 > 其他技术问题 > 正文

Java: 链表head取出用后,置next=null为何可以加速gc?

下面是LinkedList中的unlinkFirst方法源码
其中f是链表的head
里面说f.next = null; 是有利于gc
我不太理解,f此时已经ROOT不可达
必定要gc的
将其next置为null为何有助于gc?

    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

回答:

参考一篇文章: https://www.javaspecialists.e…

如有openjdk源码,则可用如下命令查找添加// help GC的时间和rev号

hg annotate -ufd src\java.base\share\classes\java\util\LinkedList.java

rev 66ef929d58f2 2009-11-06


可以参考《深入理解Java虚拟机》,楼主应该是有这本书的。试想一个问题,gc会检测GC Roots。切记,是Roots!

GC Roots是由多个Root组成的。现代JVM使用的是准确性GC。达到能够触发GC停顿的Safepoint时,并不会检查所有上下文和全局引用的Root。而是通过OopMap数据结构计算出对象偏移量和类型。在OopMap的辅助下,HotSpot可以快速地检查GC Roots。

按照常规逻辑来说,不设置f.next = null确实没有问题,但是楼主从另一个角度试想一下。
如果我从头部删除一个元素之后。又删除了一次头部元素呢??
那么将形成:

f1.next = f2
f2.next = head

f2是被f1引用的,f1未被引用 (不考虑外部持有的情况下)


在调用完unlink之后,当前调用的线程或者方法并未达到能够触发GC停顿的Safepoint。那么刚刚删除掉的f并没有立刻回收。
此时创建新对象的时候:

  1. 如果内存不够且新生代中能够释放足够的空间,则新对象直接放入。
  2. 如果内存不够存放新对象,新生代对象会被提升至老生代。腾出空间将放入新对象

问题就在这里了。综上所述,如未设置f.next = null。在未触发Safepoint的情况下连续删除两次头部元素。f1和f2之间将存在引用关系。
如有新对象需要创建且新生代内存不足时。f2将被提升至老生代而不是立刻被回收。

补充:

  1. 不设置为null并不会导致内存泄露问题。
  2. 对GC优化极其细微,应当考虑目前的GC实现。引用原文中使用的JDK1.4,原始代码在JDK12且内存足够的情况下无明显差别

专三党,如有纰漏望各位前辈指正

回答:

f 并不一定不可达
比如ListItr就有可能持有 f 的引用
如果没有这行

f.next = null; // help GC

有可能当LinkedList不再使用需要GC时,因为有别的对象持有 ListItr,ListItr 又持有 f
f.next 又指向其他节点,导致 LinkedList 里面的节点不会被GC,内存泄漏。

这是我的理解,有不对的请指正。

用代码补充一下

// 假设里面添加很多元素
LinkedList<?> list = new LinkedList(); 

// 看源码会发现这里 iterator持有了list的first Node的引用
ListIterator<?> iterator = list.listIterator(0); 

list.removeFirst();
list = null; // list以及list里面所有节点会被GC,原来的first节点还存在(被iterator引用)

// 但是假如没有 f.next = null; // help GC这行,则其他节点不会被GC,因为原来的first节点的next指向这些节点,而iterator持有first的引用
// list里面所有的Node需要等iterater被GC时才会被GC。如果是静态变量持有了iterator,这时候不就内存溢出了吗?

本文地址:H5W3 » Java: 链表head取出用后,置next=null为何可以加速gc?

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址