在Java中使用静态数据软引用防止内存不足

在Java中使用静态数据软引用防止内存不足,java,memory-management,memory-leaks,static,Java,Memory Management,Memory Leaks,Static,我有一个静态成员的类,如下所示: class C { static Map m=new HashMap(); { ... initialize the map with some values ... } } class C { static volatile SoftReference<Map> m=null; static Map getM() { Map ret; if(m == null || (ret = m.get()) ==

我有一个静态成员的类,如下所示:

class C
{
  static Map m=new HashMap();
  {
    ... initialize the map with some values ...
  }
}
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;
  }
}
顺便说一句,这实际上会消耗内存直到程序结束。我在想,我是否可以用软引用来解决它,比如:

class C
{
  static Map m=new HashMap();
  {
    ... initialize the map with some values ...
  }
}
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;
  }
}
C类
{
静态易失性软参考m=null;
静态映射getM(){
地图检索;
如果(m==null | |(ret=m.get())==null){
ret=新的HashMap();
…初始化映射。。。
m=新的软参考(ret);
}
返回ret;
}
}
问题是

  • 这种方法(和实现)正确吗
  • 如果是的话,它在真实情况下会有回报吗

  • 这张地图有多大?这样处理是否值得?你有没有测量过它的内存消耗量(就它的价值而言,我相信上面的内容一般都是可以的,但我关于优化的第一个问题是“它真正为我节省了什么”)

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


    我将同步上述内容,以防map被垃圾收集,两个线程同时命中
    getMap()
    。否则,您将同时创建两个贴图

    如果您对
    getM
    的访问是单线程的,并且它只充当缓存,那么这是可以的。
    更好的替代方法是使用固定大小的缓存,因为这提供了一致的好处。

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

    第二,虽然它在理论上是可行的,但我怀疑是否有一个现实的场景能让它获得回报。想一想:为了让它有用,地图的内容必须是:

  • 足够大,因此它们的内存使用是相关的
  • 能够在没有不可接受的延迟的情况下动态地重新创建
  • 仅在程序的其他部分需要更少内存时使用-否则所需的最大内存将是相同的,只有平均内存会更少,并且您可能在JVM之外看不到这一点,因为它非常不情愿地将堆内存返回给操作系统
  • 这里,1。二,。有点矛盾-创建大型对象也需要更长的时间。

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

    也许你正在寻找?然后可以分别对映射中的条目进行垃圾收集

    尽管根据我的经验,这并没有多大帮助,所以我使用构建了一个LRU缓存。优点是我可以控制大小,这样它就不会太大,而且仍然有用

    我在想,我是否可以用软引用来解决它

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

    无论如何

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

  • 使用软引用的一个原因是为了避免内存耗尽,因为除了在VM抛出
    OutOfMemoryError
    之前清除它们之外,没有其他约定。因此,除了在首次使用缓存之前不创建缓存之外,这种方法没有任何保证的好处


  • 关于代码,我注意到的第一件事是它混合了泛型和原始类型。那只会导致一片混乱。JDK7中的javac具有
    -Xlint:rawtypes
    功能,可以在出现问题之前快速发现此类错误

    代码不是线程安全的,但使用静态,因此跨所有线程发布。您可能不希望它是同步的,因为在多线程机器上争用会导致问题

    对整个缓存使用
    SoftReference
    的一个问题是,清除引用时会导致峰值。在某些情况下,最好使用
    ThreadLocal
    ,这样可以分散尖峰并有助于线程安全,但代价是不在线程之间共享

    但是,创建更智能的缓存更为困难。通常,最终的结果是值引用键。有很多方法可以解决这一点,这是一片混乱。我不认为蜉蝣(本质上是一对链接的
    Reference
    s)会成为JDK7。你可能会发现谷歌收藏值得一看(尽管我没有)


    java.util.LinkedHashMap
    提供了一种简单的方法来限制缓存条目的数量,但是如果您不能确定条目有多大,它就没有多大用处,如果它停止收集大型对象系统,例如
    ClassLoader
    s,则可能会导致问题。有人说你不应该让缓存逐出由垃圾收集器随意决定,但也有人说你不应该使用GC。

    WeakHashMap的问题在于,它是弱引用的键,而不是值。这对于针对
    HttpSession
    保存数据之类的事情很有用,但对于缓存则不太有用。还请注意,当没有强引用时,JVM将立即收集弱引用对象,而当内存不足时,JVM将只收集
    SoftReference
    。攻击性垃圾收集解释了它为什么不工作。不幸的是,似乎没有一个标准的SoftHashMap。
    WeakReference
    s对于缓存来说是非常危险的。根据我的经验,NetBeans在使用了一段时间后,只需坐在那里,CPU 100%执行文件访问