当前位置: 首页 > 知识库问答 >
问题:

垃圾回收机制中的非直观对象驱逐

鲁博赡
2023-03-14

我正在调试内存泄漏,不得不深入CompletableFuture内部。有这样一段代码(completablefuture . unicomposestage):

CompletableFuture<V> g = f.apply(t).toCompletableFuture();
...
CompletableFuture<V> d = new CompletableFuture<V>();
UniRelay<V> copy = new UniRelay<V>(d, g);
g.push(copy);
copy.tryFire(SYNC);
return d;

代码本身对我来说很清楚:应用一个返回完成阶段(g)的函数,创建一个中继,最终将值转移到另一个可完成的未来(d),然后返回另一个未来(d)。我看到以下参考情况:

  • 复制同时引用dg(并且构造函数中没有魔法,只有字段分配)
  • g引用复制
  • d没有引用任何内容

只有d被返回,所以,事实上,gCopy对我来说都是内部方法变量,(乍一看)永远不应该离开方法并最终被gc。天真的测试和它是由久经考验的开发人员很久以前编写的事实都表明我错了,错过了一些东西。是什么原因使这些对象从垃圾回收机制中被省略?

共有1个答案

徐鸿达
2023-03-14

在引用的代码中,没有什么可以阻止这些期货的垃圾回收,也没有必要。有问题的代码适用于以下情况:第一个可完成未来实例)已完成,而直接计算的撰写函数返回的可完成未来尚未完成。

现在,有两种可能的情况

>

  • 正在进行完成尝试。然后,最终完成未来的代码将保留对它的引用,当完成时,它将触发相关阶段的完成(通过g.push(copy)注册)。在这种情况下,依赖阶段不需要保存对其先决阶段的引用。

    这是一种一般模式。如果有一个链 x --将完成→ y,则不会有从 yx 的引用

    没有对< code>CompletableFuture实例< code>g的其他引用,且< code>g尚未完成。在这种情况下,它将永远不会完成,在内部持有对< code>g的引用也不会改变这一点。那只会浪费资源。

    以下示例程序将对此进行说明:

    public static void main(String[] args) throws Throwable {
        ReferenceQueue<Object> discovered = new ReferenceQueue<>();
        Set<WeakReference<?>> holder = new HashSet<>();
    
        CompletableFuture<Object> initial = CompletableFuture.completedFuture("somevalue");
    
        CompletableFuture<Object> finalStage = initial.thenCompose(value -> {
            CompletableFuture<Object> lost = new CompletableFuture<>();
            holder.add(new WeakReference<>(lost, discovered));
            return lost;
        });
        waitFor(finalStage, holder, discovered);
        finalStage = initial.thenCompose(value -> {
            CompletableFuture<Object> saved = CompletableFuture.supplyAsync(()-> {
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
                return "newvalue";
            });
            holder.add(new WeakReference<>(saved, discovered));
            return saved;
        });
        waitFor(finalStage, holder, discovered);
    }
    private static void waitFor(CompletableFuture<Object> f, Set<WeakReference<?>> holder,
                        ReferenceQueue<Object> discovered) throws InterruptedException {
        while(!f.isDone() && !holder.isEmpty()) {
            System.gc();
            Reference<?> removed = discovered.remove(100);
            if(removed != null) {
                holder.remove(removed);
                System.out.println("future has been garbage collected");
            }
        }
        if(f.isDone()) {
            System.out.println("stage completed with "+f.join());
            holder.clear();
        }
    }
    

    传递给thenCompose的第一个函数创建并返回一个新的未完成的CompletableFuture,而不尝试完成它,也不保存或存储对它的任何其他引用。相反,第二个函数通过Supply yAsync创建CompletableFuture,提供一个供应商,它将在一秒钟后返回一个值。

    在我的系统上,它一直在打印

    future has been garbage collected
    stage completed with newvalue
    

    表明废弃的未来不会被阻止垃圾收集,而另一个将至少保留到完成。

  •  类似资料:
    • 本文向大家介绍Python垃圾回收机制?相关面试题,主要包含被问及Python垃圾回收机制?时的应答技巧和注意事项,需要的朋友参考一下 引用计数 标记清除 分代回收  

    • 本文向大家介绍JS的垃圾回收机制?相关面试题,主要包含被问及JS的垃圾回收机制?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: GC(garbage collection),GC执行时,中断代码,停止其他操作,遍历所有对象,对于不可访问的对象进行回收,在V8引擎中使用两种优化方法, 分代回收,2、增量GC,目的是通过对象的使用频率,存在时长来区分新生代和老生代对象,多回收新生代区,少回收老

    • 主要内容:总结通过前几节对可利用空间表进行动态存储管理的介绍,运行机制可以概括为: 当用户发出申请空间的请求后,系统向用户分配内存;用户运行结束释放存储空间后,系统回收内存。这两部操作都是在用户给出明确的指令后,系统对存储空间进行有效地分配和回收。 但是在实际使用过程中,有时会因为用户申请了空间,但是在使用完成后没有向系统发出释放的指令,导致存储空间既没有被使用也没有被回收,变为了 无用单元或者会产生 悬挂访问

    • Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。 1 引用计数 PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象

    • 在约瑟夫·阿尔巴哈里(Joseph Albahari)的《C#5.0简言之》一书中,我发现了这一点 然而,根据加州大学伯克利分校的这堂课,只要堆栈上存在对该对象的引用,它就不会被垃圾收集。我的理解是,在方法返回之前,变量将保留在堆栈上。这意味着它引用的任何对象在方法返回之前都是活动的。 这是书中的错误还是java和。net垃圾收集工作有什么不同?

    • 问题内容: 我要求显式垃圾回收器调用。但是窗口并没有从屏幕上消失,为什么垃圾回收器不回收JFrame的对象? 问题答案: 当被创建时,它注册本身在一些内部摇摆的数据结构,其允许它接收到类似的鼠标点击的事件。这意味着在您告诉Swing使用摆脱窗口之前,存在对潜伏在某处的对象的引用。