1

我的项目遇到了一些内存问题,因此我决定对某些部分进行压力测试以查看一些性能测量结果。我正在使用 Google 的ConcurrentLinkedHashMap库作为 LRU 内存缓存。我的测试代码的相关部分如下所示:

final ConcurrentLinkedHashMap cache = new ConcurrentLinkedHashMap.Builder<Long,Long>()
            .maximumWeightedCapacity(200000)
            .build();

for (int i = 0; i < 10; i++) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            int count = 0;
            while (count < 1000000) {
                if (throttle) {
                    Thread.sleep(1000);
                    continue;
                }
                cache.put(random.nextLong(), random.nextLong());
                count++;
            }
        }
    }).start();
}

this.wait();

我将throttle标志设置为true内存达到 50% 以上。我有一个监控线程,每秒进行一些测量2。这是我得到的数字:

Size: 423902    Weighted Size: 200001   Memory: 0.11229571913729916
Size: 910783    Weighted Size: 200001   Memory: 0.25812696264655144
Size: 1373394   Weighted Size: 200001   Memory: 0.38996117352719034
Size: 2120239   Weighted Size: 200001   Memory: 0.6203725762957892
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212
Size: 2114424   Weighted Size: 200000   Memory: 0.6233790564491212

由于某种原因,我没有看到evictedLRU 缓存的条目被清理。我听说手动调用System.gc()是个坏主意。如果是这样,有效清理内存的好方法是什么?

旁注:有谁知道退货有什么size()用?返回正确的值,但我担心会返回更大的值..ConcurrentLinkedHashMapweightedSize()size

4

2 回答 2

3

正如您的数字所示,缓存并不严格保证最大值,但会尝试保持该高水位线。如果它确实做出了强有力的保证,那么它将通过对每个写入操作进行阻塞调用来限制并发性,并且无法有效地维护顶级 LRU 策略。在 Guava 中Cache,我们确实通过使用锁定条带来实现这一限制,但这种权衡是有局限性的。

由于 CLHM 异步连接哈希表和 LRU 数据结构,这两个数字可能会发生偏差。size是由装饰来衡量的ConcurrentHashMap。这是在任何给定时刻对键值对的最准确估计。当weighted sizeLRU 策略针对其数据结构重放 map 操作以确定新近度排序并执行驱逐时,它会计算出。加权大小保持在预期的最大值,因为当超过阈值时策略将驱逐。

预计突发大小会有所不同,但缓存将始终尝试快速自我纠正。在您的示例中,由于操作系统会为较长的时间片安排线程,因此行为会更加严重。这允许它执行比实际场景中更多的插入。摊销的 LRU 管理意味着驱逐无法跟上,因为它在任何给定时刻从一个线程中窃取了一点时间。为了更好地模拟实际行为,MemoryLeakTest强制在操作之间进行上下文切换。

如果您实施驱逐侦听器,您应该会看到计数增加并且您已达到稳定状态。如果您强制执行上下文切换,您应该会看到大小保持在下限。如果您想要对地图中可能的条目总数进行更严格的限制,那么更喜欢 Guava 实现。

于 2013-05-02T09:18:51.043 回答
0

默认的驱逐策略不是先进先出吗?如果是这样,您是否正在查看第一个插入以看到它们被驱逐?如果您正在查看最后一个,您将看不到它们。

这是来自文档:

默认
 * weighter 为每个值分配 1 的权重,以通过
 * 键值对的总数。包含集合的地图可以选择
 * 按集合中元素的数量加权值并绑定地图
 * 按其包含的元素总数。改变一个值
 * 修改其权重需要对
 * 地图。

因此,根据我对它的阅读,默认情况下加权大小不会改变。

于 2013-05-02T02:49:48.237 回答