java - ConcurrentLinkedQueue$Node 在 remove() 之后保留在堆中

标签 java concurrency heap-memory java.util.concurrent

我有一个写入和读取 ConcurrentLinkedQueue 的多线程应用程序,它在概念上用于支持列表/表中的条目。我最初为此使用了 ConcurrentHashMap,效果很好。一项新要求需要跟踪进入的订单条目,因此可以根据某些条件以最旧的顺序删除它们。 ConcurrentLinkedQueue 似乎是一个不错的选择,而且在功能上它运行良好。

可配置数量的条目保存在内存中,当达到限制时提供新条目时,将以最早的优先顺序搜索队列以查找可以删除的条目。某些条目不会被系统删除并等待客户端交互。

似乎正在发生的事情是我在发生的队列前面有一个条目,比如 100K 条目之前。该队列似乎配置的条目数量有限(size() == 100),但在分析时,我发现内存中有约 100K 个 ConcurrentLinkedQueue$Node 对象。这似乎是设计使然,只需看一眼 ConcurrentLinkedQueue 的源代码,删除操作只会删除对正在存储的对象的引用,但会保留链表以供迭代。

最后我的问题是:是否有一种“更好”的懒惰方式来处理这种性质的集合?我喜欢 ConcurrentLinkedQueue 的速度,我只是无法承受在这种情况下似乎可能出现的无限泄漏。如果没有,我似乎必须创建第二个结构来跟踪订单并且可能会遇到相同的问题,加上同步问题。

最佳答案

这里实际发生的是 remove 方法准备一个轮询线程来清空链接引用。

ConcurrentLinkedQueue 是一个非阻塞线程安全队列实现。但是,当您尝试从队列中轮询节点时,它是一个具有两个功能的过程。首先将值置空,然后将引用置空。 CAS 操作是单个原子函数,不会为轮询提供即时解决方案。

轮询时发生的事情是,第一个成功的线程将获取节点的值并将该值清空,然后该线程将尝试清空引用。另一个线程可能会进入并尝试从队列中进行轮询。为确保此 Queue 拥有非阻塞属性(即一个线程的失败不会导致另一个线程的失败),新的传入线程将查看该值是否为 null,如果为 null,则该线程将使该引用为 null 并尝试再次轮询()。

所以您在这里看到的是删除线程只是准备任何新的轮询线程以使引用为空。尝试实现非阻塞删除功能我认为几乎是不可能的,因为这需要三个原子功能。值的 null 是对所述节点的 null 引用,最后是从该节点的父节点到其后继节点的新引用。

回答你的最后一个问题。不幸的是,没有更好的方法来实现删除和维护队列的非阻塞状态。至少在这一点上是这样。一旦处理器开始推出 2 路和 3 路套管,那么这是可能的。

关于java - ConcurrentLinkedQueue$Node 在 remove() 之后保留在堆中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2547161/

相关文章:

java - Java中的位总和

java - 如果输入无效,如何让程序重复而不是继续无效输入?

java - 使用 notify/notifyAll 增加/减少一个变量

Android Studio Profiler 显示空 Activity 的内存使用量为 100 MB

heap-memory - 堆转储因 jemalloc mcllctl 而失败

java - JPA:一张表有两个不同的数据库

java - 最高温度Mapreduce Java代码中的运行时错误

java - 使用 ConcurrentHashMap 进行余额存储等操作是否安全

ios - 如何在正确的队列上初始化 ManagedObjectContext?

c++ - 不使用 new 为指针赋值