简体   繁体   English

对静态数据使用SoftReference来防止Java中的内存不足

[英]Using SoftReference for static data to prevent memory shortage in Java

I have a class with a static member like this: 我有一类这样的静态成员:

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

AFAIK, this would consume memory practically to the end of the program. AFAIK,这实际上将消耗内存到程序结尾。 I was wondering, if I could solve it with soft references, like this: 我想知道是否可以通过软引用来解决它,如下所示:

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;
  }
}

The question is 问题是

  1. is this approach (and the implementation) right? 这种方法(和实现)对吗?
  2. if it is, does it pay off in real situations? 如果是这样,它在实际情况下是否有回报?

First, the code above is not threadsafe. 首先,上面的代码不是线程安全的。

Second, while it works in theory, I doubt there is a realistic scenario where it pays off. 其次,尽管它在理论上可行,但我怀疑是否有现实的方案能奏效。 Think about it: In order for this to be useful, the map's contents would have to be: 考虑一下:为了使此功能有用,地图的内容必须为:

  1. Big enough so that their memory usage is relevant 足够大,以便与它们的内存使用量相关
  2. Able to be recreated on the fly without unacceptable delays 能够即时重新创建,不会出现无法接受的延迟
  3. Used only at times when other parts of the program require less memory - otherwise the maximum memory required would be the same, only the average would be less, and you probably wouldn't even see this outside the JVM since it give back heap memory to the OS very reluctantly. 仅在程序的其他部分需要较少的内存时使用-否则所需的最大内存将相同,仅平均内存将更少,并且您可能甚至不会在JVM之外看到此信息,因为它会将堆内存返还给该操作系统非常不情愿。

Here, 1. and 2. are sort of contradictory - large objects also take longer to create. 在这里,1.和2.有点矛盾-大对象也需要更长的时间才能创建。

This is okay if your access to getM is single threaded and it only acts as a cache. 如果您对getM的访问是单线程的并且仅充当缓存,则可以。 A better alternative is to have a fixed size cache as this provides a consistent benefit. 更好的选择是使用固定大小的高速缓存,因为这样可以提供一致的好处。

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

How big is this map going to be ? 这幅地图有多大? Is it worth the effort to handle it this way ? 以这种方式处理它值得吗? Have you measured the memory consumption of this (for what it's worth, I believe the above is generally ok, but my first question with optimisations is "what does it really save me"). 您是否已测量了此方法的内存消耗(对于它的价值,我相信上面的方法通常是可以的,但是我进行优化的第一个问题是“它实际上为我节省了多少”)。

You're returning the reference to the map, so you need to ensure that your clients don't hold onto this reference (and prevent garbage collection). 您要将参考返回给地图,因此需要确保您的客户不要保留该参考(并防止垃圾回收)。 Perhaps your class can hold the reference, and provide a getKey() method to access the content of the map on behalf of clients ? 也许您的类可以保存引用,并提供getKey()方法来代表客户端访问地图的内容? That way you'll maintain control of the reference to the map in one place. 这样一来,您将可以一站式控制对地图的引用。

I would synchronise the above, in case the map gets garbage collected and two threads hit getMap() at the same time. 如果地图被垃圾回收并且两个线程同时命中getMap() ,我将同步上面的内容。 Otherwise you're going to create two maps simultaneously! 否则,您将同时创建两个地图!

Maybe you are looking for WeakHashMap ? 也许您在寻找WeakHashMap吗? Then entries in the map can be garbage collected separately. 然后,可以分别垃圾收集地图中的条目。

Though in my experience it didn't help much, so I instead built an LRU cache using LinkedHashMap . 尽管根据我的经验,它并没有太大帮助,所以我改用LinkedHashMap构建了LRU缓存。 The advantage is that I can control the size so that it isn't too big and still useful. 好处是我可以控制大小,以免太大但仍然有用。

I was wondering, if I could solve it with soft references 我想知道是否可以通过软引用解决

What is it that you are trying to solve? 是什么,你正在试图解决? Are you running into memory problems, or are you prematurely optimizing? 您是否遇到内存问题,还是过早地进行了优化?

In any case, 任何状况之下,

  1. The implementation should be altered a bit if you were to use it. 如果要使用该实现,则应对其进行一些更改。 As has been noted, it isnt thread-safe. 如前所述,它不是线程安全的。 Multiple threads could access the method at the same time, allowing multiple copies of your collection to be created. 多个线程可以同时访问该方法,从而可以创建集合的多个副本。 If these collections were then strongly referenced for the remainder of your program you would end up with more memory consumption, not less 如果随后在程序的其余部分中强烈引用了这些集合,则最终将消耗更多的内存,而不是更少

  2. A reason to use SoftReferences is to avoid running out of memory, as there is no contract other than that they will be cleared before the VM throws an OutOfMemoryError . 使用SoftReferences的原因是为了避免耗尽内存,因为除了在VM抛出OutOfMemoryError之前其清除之外,没有其他约定。 Therefore there is no guaranteed benefit of this approach, other than not creating the cache until it is first used. 因此,除了在首次使用缓存之前不创建缓存,此方法没有其他好处。

The first thing I notice about the code is that it mixes generic with raw types. 关于代码,我注意到的第一件事是它将通用类型与原始类型混合在一起。 That is just going to lead to a mess. 那只会导致混乱。 javac in JDK7 has -Xlint:rawtypes to quickly spot that kind of mistake before trouble starts. JDK7中的javac具有-Xlint:rawtypes ,可以在出现故障之前快速发现这种错误。

The code is not thread-safe but uses statics so is published across all threads. 该代码不是线程安全的,而是使用静态的,因此会在所有线程中发布。 You probably don' want it to be synchronized because the cause problems if contended on multithreaded machines. 您可能不希望它被synchronized因为如果在多线程计算机上争执,则会引起问题。

A problem with use a SoftReference for the entire cache is that you will cause spikes when the reference is cleared. 在整个缓存中使用SoftReference一个问题是,在清除引用时,会导致峰值。 In some circumstances it might work out better to have ThreadLocal<SoftReference<Map<K,V>>> which would spread the spikes and help-thread safety at the expense of not sharing between threads. 在某些情况下,最好使用ThreadLocal<SoftReference<Map<K,V>>>来分散峰值和帮助线程安全性,但需在线程之间不共享。

However, creating a smarter cache is more difficult. 但是,创建更智能的缓存更加困难。 Often you end up with values referencing keys. 通常,您最终会得到引用键的值。 There are ways around this bit it is a mess. 有很多方法可以解决这个问题。 I don't think ephemerons (essentially a pair of linked Reference s) are going to make JDK7. 我认为星历表(本质上是一对链接的Reference )不会构成JDK7。 You might find the Google Collections worth looking at (although I haven't). 您可能会发现值得关注的Google Collections(尽管我没有)。

java.util.LinkedHashMap gives an easy way to limit the number of cached entries, but is not much use if you can't be sure how big the entries are, and can cause problems if it stops collection of large object systems such as ClassLoader s. java.util.LinkedHashMap提供了一种限制缓存条目数量的简便方法,但是如果您不能确定条目的大小,则用处不大,如果停止收集大型对象系统(如ClassLoader ,可能会引起问题秒。 Some people have said you shouldn't leave cache eviction up to the whims of the garbage collector, but then some people say you shouldn't use GC. 有人说您不应该将缓存逐出留给垃圾收集器来处理,但是有些人说您不应该使用GC。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM