强制清除记忆

时间:2013-05-02 02:38:54

标签: java memory guava out-of-memory concurrenthashmap

我的项目遇到了一些内存问题,所以我决定对某些部分进行压力测试以查看一些性能测量。我使用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();

一旦内存达到50%以上,我就会将throttle标记设置为true。我有一个监控线程,每隔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

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

单面注:有人知道size()返回的ConcurrentLinkedHashMap是什么吗? weightedSize()会返回正确的值,但我担心size会返回更大的值。

2 个答案:

答案 0 :(得分:3)

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

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

预计突发的大小不同,但缓存总是会尝试快速自我纠正。在您的示例中,行为会恶化,因为操作系统将为大时间片安排线程。这允许它在现实场景中执行比可能的更多插入。摊销的LRU管理意味着驱逐无法跟上,因为它在任何给定时刻从一个线程中偷走了一点时间。为了更好地模拟实际行为,MemoryLeakTest强制在操作之间切换上下文。

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

答案 1 :(得分:0)

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

这来自文档:

The default
 * weigher assigns each value a weight of 1 to bound the map by the
 * total number of key-value pairs. A map that holds collections may choose to
 * weigh values by the number of elements in the collection and bound the map
 * by the total number of elements that it contains. A change to a value that
 * modifies its weight requires that an update operation is performed on the
 * map.

因此,基于我的阅读量,默认情况下加权大小不会改变。