创建字节数组的缓存

时间:2013-06-12 12:12:13

标签: java garbage-collection

在我的代码中,我经常在服务器上运行,我无法控制配置,我有用户集合,每个用户都有一个byte[]数组。

有时这些byte[]数组对用户来说是唯一的。但是,通常会有大量用户使用完全相同的byte[]数组。

我正在尝试减少服务器的RAM消耗。

我已经尝试将我的byte[]数组转换为字符串并实习它们,但后来我经常遇到PERM-GEN内存不足错误。当我想为用户访问byte[]数组时,我也发现编码/解码会显着降低性能,而且我看到更糟糕的内存使用情况 - 预备字符串比数组大得多。

当Java数组不可清除时,如何进行Set<SoftReference<byte[]>>查找,而SoftReferences不会将对象的哈希包装在任何一点上。 Map<byte[],SoftReference<byte[]>>显然也在打败自己,因为关键本身并阻止收集;无论如何,Set内部实施了Map

那我怎么能实习生 byte[]数组呢?

3 个答案:

答案 0 :(得分:5)

如果您有效地拥有许多相同的数组,请使用HashSet<ByteBuffer>作为缓存。您可以使用方法array()获取ByteBuffer数组,并且ByteBuffer类具有hashCodeequals方法。当然,如果你的数组是不可变的,那就好了。

EDIT2 来自@Will的评论是准确的,为了能够取回数组,使用WeakHashMap<ByteBuffer,WeakReference<ByteBuffer>>并执行类似的操作:

public byte[] internalize(byte[] bytes) {
 ByteBuffer wrapped = ByteBuffer.wrap(bytes);
 if(cache.containsKey(wrapped)) {
  wrapped = cache.get(wrapped).get();
 }
 else {
  cache.put(wrapped, new WeakReference<ByteBuffer>(wrapped);
 }
 return wrapped.array();
}

答案 1 :(得分:2)

  

我已经尝试将我的byte []数组转换为字符串并实习它们,但之后我经常遇到PERM-GEN内存不足错误。

我同意您需要String.intern()之类的内容,但标准实施方式为native,所以没有多少乐趣。

您可以使用Map<Integer,Collection<SoftReference<byte[]>>>,使用字节数组的哈希码作为Map键。然后,您的intern方法可以使用与给定字节数组相同的代码查找现有字节数组。使用一个好的哈希代码,它应该提供一小组数组来检查匹配。


修改:澄清:

这样的事情:

 class ByteArrayCache
 {
      private final Map<Integer,Collection<SoftReference<byte[]>> map = new ...;

      public final byte[] intern(byte[] byteArray)
      {
           final int hash = Arrays.hashCode(byteArray);
           final Collection<SoftReference<byte[]>> arrays = map.get(hash);
           if (arrays != null) {
              // Search through arrays for a match, and return the match.
              // If no match found, add byteArray to the collection and return it
           } else {
              // create a new map entry, add byteArray to it, and return byte array
           }
      }
 }

答案 2 :(得分:1)

我会基于Guava弱值映射实现缓存。它保证如果没有对字节数组的强引用,则会自动删除该条目。

class Cache {
    private final ConcurrentMap<Key, byte[]> map = new MapMaker().weakValues().makeMap();

    private static class Key {
        byte[] a;
        int hash;

        Key(byte[] a) {
            this.a = a;
            hash = Arrays.hashCode(a);
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Key) {
                return Arrays.equals(a, ((Key) obj).a);
            }
            return false;
        }
    }

    public byte[] intern(byte[] a) {
        byte[] a1 = map.putIfAbsent(new Key(a), a);
        if (a1 != null) {
            return a1; 
        }
        return a;
    }
}