java - 强制清除内存

标签 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() 是个坏主意。如果是这样,有什么有效清理内存的好方法?

旁注:有谁知道 ConcurrentLinkedHashMapsize() 返回什么? weightedSize() 返回正确的值,但我担心 size 返回的值大得多..

最佳答案

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

由于 CLHM 异步连接哈希表和 LRU 数据结构,因此这两个数字可能会有所不同。 大小 由修饰的ConcurrentHashMap 测量。这是在任何给定时刻对键值对的最准确估计。 加权大小 由 LRU 策略在针对其数据结构重放映射操作以确定新近度排序并执行逐出时计算得出。加权大小保持在预期的最大值,因为当超过阈值时策略将逐出。

预计大小会因突发而异,但缓存始终会尝试快速 self 更正。在您的示例中,这种行为会加剧,因为操作系统会为一个大的时间片安排一个线程。这允许它执行比现实场景中更多的插入。摊销的 LRU 管理意味着逐出无法跟上,因为它在任何给定时刻都从一个线程中窃取了一点时间。为了更好地模拟实际行为,MemoryLeakTest强制操作之间的上下文切换。

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

关于java - 强制清除内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16329493/

相关文章:

java - SurfaceView 作为背景

java - 如何在 linux 中为 java 安装

c - C中线程之间如何共享内存

Python:列表的每个元素占用多少空间?

java - 如何根据多个标准对多重图进行索引?

java - j2objc 是否支持 Guava ?

java - JTable 最后一个值 null

java - 我怎么知道 Java 有泄漏?

c++ - 存储分配器 - 它是什么?

guava - Guava 是否带有命令行参数解析器