java - 如果达到内存大小限制,如何从 map 中删除元素?

标签 java caching memory-management guava lru

我已经使用 ConcurrentLinkedHashMap 实现了 LRU 缓存。在同一张 map 中,如果我的 map 达到特定限制,我将清除事件,如下所示。

我有一个 MAX_SIZE 变量,它相当于 3.7 GB,一旦我的 map 达到该限制,我就会从我的 map 中清除事件。

下面是我的代码:

import java.util.concurrent.ConcurrentMap;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;

// does this really equal to 3.7 GB? can anyone explain this?
public static final int MAX_SIZE = 20000000; //equates to ~3.7GB with assumption that each event is 200 bytes AVG

public static EvictionListener<String, DataObject> listener = new EvictionListener<String, DataObject>() {
    public void onEviction(String key, DataObject value) {
        deleteEvents();
    }
};
public static final ConcurrentMap<String, DataObject> holder = new ConcurrentLinkedHashMap.Builder<String, DataObject>()
            .maximumWeightedCapacity(MAX_SIZE).listener(listener).build();

private static void deleteEvents() {
    int capacity = MAX_SIZE - (MAX_SIZE * (20 / 100));
    if (holder.size() >= capacity) {
        int numEventsToEvict = (MAX_SIZE * 20) / 100;
        int counter = 0;
        Iterator<String> iter = holder.keySet().iterator();
        while (iter.hasNext() && counter < numEventsToEvict) {
            String address = iter.next();
            holder.remove(address);
            System.out.println("Purging Elements: " +address);
            counter++;
        }
    }
}

// this method is called every 30 seconds from a single background thread 
// to send data to our queue
public void submit() {
    if (holder.isEmpty()) {
        return;
    }

    // some other code here

    int sizeOfMsg = 0;
    Iterator<String> iter = holder.keySet().iterator();
    int allowedBytes = MAX_ALLOWED_SIZE - ALLOWED_BUFFER;

    while (iter.hasNext() && sizeOfMsg < allowedBytes) {
        String key = iter.next();
        DataObject temp = holder.get(key);

        // some code here

        holder.remove(key);

        // some code here to send data to queue
    }
}   

// this holder map is used in below method to add the events into it.
// below method is being called from some other place.
public void addToHolderRequest(String key, DataObject stream) {
    holder.put(key, stream);
}

下面是我为此使用的 Maven 依赖项:

<dependency>
    <groupId>com.googlecode.concurrentlinkedhashmap</groupId>
    <artifactId>concurrentlinkedhashmap-lru</artifactId>
    <version>1.4</version>
</dependency>

我不确定这样做是否正确?如果事件平均为 200 字节,这个 MAX_SIZE 真的等于 3.7 GB 吗?有没有更好的方法来做到这一点?我还有一个后台线程,每 30 秒调用一次 deleteEvents() 方法,同样的后台线程也调用 submit 方法从 holder 映射中提取数据并发送到队列。

所以想法是,在 addToHolderRequest 方法中将事件添加到 holder 映射,然后从后台每 30 秒调用一次 submit 方法,该方法将发送通过迭代此映射将数据添加到我们的队列,然后在提交方法完成后,从将清除元素的同一后台线程调用 deleteEvents() 方法。我在生产环境中运行这段代码,看起来它没有正确清除事件,而且我的 holder map 大小不断增加。我将最小/最大堆内存设置为 6GB。

最佳答案

  1. 代替估计 JVM 中对象的大小并使用强引用引用它们,您可以使用“最常用于实现内存敏感缓存”的软引用 (SoftReference)。例如CacheBuilder.softValues()来自 google/guava: Google Core Libraries for Java 6+ :“软引用对象将以全局最近最少使用的方式进行垃圾回收,以响应内存需求。”但是,我建议您首先熟悉 CachesExplained · google/guava Wiki (特别是 Reference-based Eviction 部分)。
  2. 作为使用软引用的一种调整,您还可以尝试“受害者缓存方法”,如 here 所述。它使用“驱逐到 [a] 软缓存的普通缓存,并在可能的情况下恢复未命中的条目”。
  3. 如果您确定要实际估​​算物体的大小,请查看 Ehcache及其 Sizing Storage Tiers .它有 Built-In Sizing Computation and Enforcement用于内存有限的缓存。

关于java - 如果达到内存大小限制,如何从 map 中删除元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35078202/

相关文章:

java - 24 小时模式下的 Vaadin DateField

Java 8 Lambda 处理 > if > else

ios - 如何在我的项目中使用 SDWebImage

asp.net-mvc - 为什么输出缓存不适用于我的 ASP.NET MVC 4 应用程序?

c++ - 复制构造函数中的内存泄漏

java - 使用递归 Java 查找列表中的最大数字

java - 使用java将输出写入命令提示符

java - 如何缩短 Gradle 依赖缓存文件夹名称?

java - 找出java中的内存分配热点

c++ - 是否可以在运行时在堆上创建一个数组,然后在需要时分配更多空间?