我有这样一个静态成员的类:
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;
}
}
问题是
- 就是这种方法(和执行),对不对?
- 如果是,那么它在实际生活中还清?
首先,上面的代码不是线程。
其次,虽然它在理论上,我怀疑有它不负有心人,一个真实的场景。 想想看:为了使这是有用的,地图的内容必须是:
- 足够大,它们的内存使用情况是相关的
- 能够在没有不可接受的延迟即时重建
- 仅在次使用时,程序的其他部分需要较少的内存 - 另有规定的最大内存是一样的,只是一般会少些,你可能甚至看不到这个JVM之外,因为它给回堆内存操作系统很无奈。
在这里,1和2有几分矛盾 - 大型物体还需要更长的时间去创造。
这是正常的,如果你的访问getM
是单线程的,它只是充当缓存。 更好的选择是有一个固定大小的缓存,因为这提供了一致的利益。
getM()
应被synchronized
,以避免m
在不同线程同时被初始化。
这个地图有多大要多大? 是否值得这样处理的努力? 你有没有测量该内存消耗(对于它的价值,我相信上面的一般是好的,但我与最佳化的第一个问题是“它到底救我”)。
您传回参照地图,所以你需要确保你的客户不保留此引用(防止垃圾收集)。 也许你的类可以保持参考,并提供信息getKey()方法来访问客户代表的地图的内容? 这样,你会保持在一个地方的参考地图的控制权。
我会同步以上,如果地图上得到垃圾收集,两个线程击中getMap()
在同一时间。 否则,你要同时创建两个地图!
也许你正在寻找WeakHashMap的 ? 然后,在映射条目可以被垃圾分开收集。
虽然在我的经验,它并没有太大的帮助,所以我代替内置采用LRU缓存的LinkedHashMap 。 其优点是,我可以控制大小,因此它不是太大,还是有用的。
我在想,如果我可以用软引用解决它
它是什么,你正在试图解决? 您是否在运行到内存的问题,或者是你过早优化?
任何状况之下,
如果你要使用它的实施应该改变一下。 正如已经指出的那样,它不是线程安全的。 多个线程可以同时访问方法,允许创建您的收藏的多个副本。 如果这些集合然后极力为你的程序的其余部分中引用你最终会与更多的内存消耗,而不是更少
使用SoftReferences一个原因是为了避免耗尽内存,因为没有比VM抛出之前,他们将被清除其他合同OutOfMemoryError
。 因此,有没有保证这种做法的好处,比未创建缓存,直到它被首次使用其他。
我注意到关于代码的第一件事是,它一般混合使用原始类型。 这仅仅是将导致混乱。 javac的JDK7中有-Xlint:rawtypes
麻烦开始之前就可以快速发现那种错误的。
该代码是不是线程安全的,但是使用静态所以所有线程出版。 你可能不要希望它是synchronized
,因为事业的问题,如果在多线程机器争辩。
与使用的问题SoftReference
整个缓存,您会导致尖峰当参考被清除。 在某些情况下,它可能会制定出最好有ThreadLocal<SoftReference<Map<K,V>>>
这会蔓延尖峰和帮助线程安全的在线程之间没有共享的费用。
但是,创建一个更聪明的缓存是比较困难的。 通常,你最终值引用键。 这种情况有解决办法位是一个烂摊子。 我不认为ephemerons(实质上是对链接的Reference
S)将会使JDK7。 您可能会发现谷歌集合值得看的(虽然我没有)。
java.util.LinkedHashMap
给出了一个简单的方法来限制缓存条目的数量,但没有太大用处,如果你不能确定的条目有多大,如果它停止大对象系统,如收集可能会导致问题ClassLoader
秒。 有些人说,你不应该离开的缓存驱逐到垃圾收集的冲动,但后来有人说你不应该使用GC。