使用SoftReference进行静态数据以防止Java中的内存不足

时间:2009-07-12 11:09:24

标签: java memory-management memory-leaks static

我有一个像这样的静态成员的类:

class C
{
  static Map m=new HashMap();
  {
    ... initialize the map with some values ...
  }
}

AFAIK,这会在程序结束时消耗内存。我想知道,如果我能用软引用解决它,就像这样:

class C
{
  static volatile SoftReference<Map> m=null;
  static Map getM() {
    Map ret;
    if(m == null || (ret = m.get()) == null) {
      ret=new HashMap();
      ... initialize the map ...
      m=new SoftReference(ret);
    }
    return ret;
  }
}

问题是

  1. 这种方法(和实施)对吗?
  2. 如果是的话,是否会在实际情况下获得回报?

7 个答案:

答案 0 :(得分:4)

首先,上面的代码不是线程安全的。

其次,虽然它在理论上有效,但我怀疑是否有一个现实的情况,它会得到回报。想一想:为了使它有用,地图的内容必须是:

  1. 足够大,以便他们的内存使用相关
  2. 能够在没有不可接受的延迟的情况下重新创建
  3. 仅在程序的其他部分需要较少内存时使用 - 否则所需的最大内存将相同,只有平均值会更少,并且您可能甚至不会在JVM之外看到它,因为它回馈非常不情愿地向操作系统堆内存。
  4. 在这里,1.和2.有点矛盾 - 大型物体也需要更长的时间来创作。

答案 1 :(得分:2)

如果您对getM的访问权限是单线程的,并且它只充当缓存,那么这是可以的。 更好的选择是拥有固定大小的缓存,因为这可以提供一致的好处。

答案 2 :(得分:1)

getM()应该是synchronized,以避免m被不同的线程同时初始化。

答案 3 :(得分:0)

这张地图有多大?用这种方式处理它是否值得?你有没有测量过它的内存消耗量(对于它的价值,我相信上面的内容一般都没问题,但我的第一个优化问题是“它真的能拯救我”)。

您将返回对地图的引用,因此您需要确保您的客户端不会保留此引用(并防止垃圾回收)。也许你的类可以保存引用,并提供一个getKey()方法来代表客户端访问地图的内容?这样你就可以在一个地方保持对地图参考的控制。

如果地图被垃圾收集并且两个线程同时点击getMap(),我会同步上面的内容。否则你将同时创建两个地图!

答案 4 :(得分:0)

也许您正在寻找WeakHashMap?然后地图中的条目可以单独进行垃圾收集。

虽然根据我的经验它没有多大帮助,所以我改为使用LinkedHashMap构建一个LRU缓存。优点是我可以控制尺寸,使其不会太大而且仍然有用。

答案 5 :(得分:0)

  

我想知道,如果我能用软参考解决

您想要解决的是什么?您是否遇到内存问题,或者您是否过早地进行了优化?

无论如何,

  1. 如果您要使用它,应该稍微改变一下。如前所述,它不是线程安全的。多个线程可以同时访问该方法,允许创建集合的多个副本。如果这些集合随后被强烈引用到程序的其余部分,那么最终会消耗更多内存,而不是更少

  2. 使用SoftReferences的一个原因是为了避免内存不足,因为除了在VM抛出OutOfMemoryError之前 之前没有合同。因此,除了在首次使用之前不创建缓存,这种方法没有保证的好处。

答案 6 :(得分:0)

我注意到代码的第一件事是它将泛型与原始类型混合在一起。这只会导致一团糟。 JDK7中的javac有-Xlint:rawtypes可以在故障开始之前快速发现这种错误。

代码不是线程安全的,但使用静态代码,因此在所有线程中发布。您可能不希望它为synchronized,因为如果多线程计算机上存在争议则会导致问题。

对整个缓存使用SoftReference的问题是,在清除引用时会导致峰值。在某些情况下,使用ThreadLocal<SoftReference<Map<K,V>>>可以更好地解决峰值和帮助线程安全,但代价是不在线程之间共享。

但是,创建更智能的缓存更加困难。通常,您最终会得到引用键的值。有一些方法可以解决这个问题。我不认为ephemerons(基本上是一对链接的Reference)将制作JDK7。您可能会发现值得关注的Google收藏集(虽然我没有)。

java.util.LinkedHashMap提供了一种简单的方法来限制缓存条目的数量,但是如果您无法确定条目的大小,并且如果它停止收集大型对象系统可能会导致问题,则没有太大用处例如ClassLoader s。有些人说你不应该把缓存驱逐放到垃圾收集器的一时兴起,但有些人说你不应该使用GC。