Guide to WeakHashMap in Java – Java中的WeakHashMap指南

最后修改: 2017年 2月 9日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we will be looking at a WeakHashMap from the java.util package.

在这篇文章中,我们将看到一个WeakHashMap,它来自java.util包。

In order to understand the data structure, we’ll use it here to roll out a simple cache implementation. However, keep in mind that this is meant to understand how the map works, and creating your own cache implementation is almost always a bad idea.

为了理解这个数据结构,我们将在这里使用它来推出一个简单的缓存实现。然而,请记住,这只是为了理解地图是如何工作的,而创建你自己的缓存实现几乎总是一个坏主意。

Simply put, the WeakHashMap is a hashtable-based implementation of the Map interface, with keys that are of a WeakReference type.

简单地说,WeakHashMapMap接口的基于哈希图的实现,其键是WeakReference类型。

An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use, meaning that there is no single Reference that point to that key. When the garbage collection (GC) process discards a key, its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.

WeakHashMap中的条目将在其键不再被正常使用时自动删除,这意味着没有一个参考指向该键。当垃圾收集(GC)过程丢弃一个键时,它的条目就被有效地从地图中移除,所以这个类的行为与其他Map实现有些不同。

2. Strong, Soft, and Weak References

2.强、软、弱的引用

To understand how the WeakHashMap works, we need to look at a WeakReference class – which is the basic construct for keys in the WeakHashMap implementation. In Java, we have three main types of references, which we’ll explain in the following sections.

为了理解WeakHashMap如何工作,我们需要看看WeakReference–这是WeakHashMap实现中键的基本构造。在Java中,我们有三种主要的引用类型,我们将在下面的章节中解释。

2.1. Strong References

2.1.强有力的参考文献

The strong reference is the most common type of Reference that we use in our day to day programming:

强参考是我们在日常编程中最常见的参考类型。

Integer prime = 1;

The variable prime has a strong reference to an Integer object with value 1. Any object which has a strong reference pointing to it is not eligible for GC.

变量prime有一个强引用到一个Integer对象,值为1。任何有强引用指向的对象都没有资格进入GC。

2.2. Soft References

2.2.软参考资料

Simply put, an object that has a SoftReference pointing to it won’t be garbage collected until the JVM absolutely needs memory.

简单地说,具有SoftReference指向的对象在JVM绝对需要内存时才会被垃圾回收。

Let’s see how we can create a SoftReference in Java:

让我们看看如何在Java中创建一个SoftReference

Integer prime = 1;  
SoftReference<Integer> soft = new SoftReference<Integer>(prime); 
prime = null;

The prime object has a strong reference pointing to it.

prime对象有一个指向它的强引用。

Next, we are wrapping prime strong reference into a soft reference. After making that strong reference null, a prime object is eligible for GC but will be collected only when JVM absolutely needs memory.

接下来,我们将prime强引用包装成一个软引用。在使该强引用null之后,prime对象就有资格进入GC,但只有在JVM绝对需要内存时才会被收集。

2.3. Weak References

2.3.弱的引用

The objects that are referenced only by weak references are garbage collected eagerly; the GC won’t wait until it needs memory in that case.

那些只被弱引用引用的对象会被急切地收集;在这种情况下,GC不会等到需要内存的时候才去收集。

We can create a WeakReference in Java in the following way:

我们可以通过以下方式在Java中创建一个WeakReference

Integer prime = 1;  
WeakReference<Integer> soft = new WeakReference<Integer>(prime); 
prime = null;

When we made a prime reference null, the prime object will be garbage collected in the next GC cycle, as there is no other strong reference pointing to it.

当我们让一个prime引用null时,prime对象将在下一个GC周期被垃圾回收,因为没有其他强引用指向它。

References of a WeakReference type are used as keys in WeakHashMap.

WeakReference类型的引用被用作WeakHashMap中的键。

3. WeakHashMap as an Efficient Memory Cache

3.WeakHashMap作为高效的内存缓存

Let’s say that we want to build a cache that keeps big image objects as values, and image names as keys. We want to pick a proper map implementation for solving that problem.

比方说,我们想建立一个缓存,把大的图像对象作为值,把图像名称作为键。我们想挑选一个合适的地图实现来解决这个问题。

Using a simple HashMap will not be a good choice because the value objects may occupy a lot of memory. What’s more, they’ll never be reclaimed from the cache by a GC process, even when they are not in use in our application anymore.

使用一个简单的HashMap将不是一个好的选择,因为值对象可能会占用大量的内存。更重要的是,它们永远不会被GC进程从缓存中回收,即使它们在我们的应用程序中不再被使用。

Ideally, we want a Map implementation that allows GC to automatically delete unused objects. When a key of a big image object is not in use in our application in any place, that entry will be deleted from memory.

理想情况下,我们想要一个Map实现,允许GC自动删除未使用的对象。当一个大图像对象的一个键在我们的应用程序中的任何地方不使用时,该条目将被从内存中删除。

Fortunately, the WeakHashMap has exactly these characteristics. Let’s test our WeakHashMap and see how it behaves:

幸运的是,WeakHashMap正好具有这些特性。让我们测试一下我们的WeakHashMap,看看它的表现如何。

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();
BigImage bigImage = new BigImage("image_id");
UniqueImageName imageName = new UniqueImageName("name_of_big_image");

map.put(imageName, bigImage);
assertTrue(map.containsKey(imageName));

imageName = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

We’re creating a WeakHashMap instance that will store our BigImage objects. We are putting a BigImage object as a value and an imageName object reference as a key. The imageName will be stored in a map as a WeakReference type.

我们正在创建一个WeakHashMap实例,它将存储我们的BigImage对象。我们将一个BigImage对象作为一个值,将一个imageName对象引用作为一个键。imageName将作为WeakReference类型被存储在一个地图中。

Next, we set the imageName reference to be null, therefore there are no more references pointing to the bigImage object. The default behavior of a WeakHashMap is to reclaim an entry that has no reference to it on next GC, so this entry will be deleted from memory by the next GC process.

接下来,我们将imageName引用设置为null,因此不再有指向bigImage对象的引用。WeakHashMap的默认行为是在下次GC时回收没有引用的条目,所以这个条目将被下次GC进程从内存中删除。

We are calling a System.gc() to force the JVM to trigger a GC process. After the GC cycle, our WeakHashMap will be empty:

我们正在调用System.gc() 来强制JVM触发一个GC过程。在GC循环之后,我们的WeakHashMap将是空的。

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();
BigImage bigImageFirst = new BigImage("foo");
UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image");

BigImage bigImageSecond = new BigImage("foo_2");
UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2");

map.put(imageNameFirst, bigImageFirst);
map.put(imageNameSecond, bigImageSecond);
 
assertTrue(map.containsKey(imageNameFirst));
assertTrue(map.containsKey(imageNameSecond));

imageNameFirst = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.size() == 1);
await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.containsKey(imageNameSecond));

Note that only the imageNameFirst reference is set to null. The imageNameSecond reference remains unchanged. After GC is triggered, the map will contain only one entry – imageNameSecond.

注意,只有imageNameFirst引用被设置为nullimageNameSecond引用保持不变。在GC被触发后,地图将只包含一个条目–imageNameSecond

4. Conclusion

4.总结

In this article, we looked at types of references in Java to fully understand how java.util.WeakHashMap works. We created a simple cache that leverages behavior of a WeakHashMap and test if it works as we expected.

在这篇文章中,我们研究了Java中的引用类型,以充分了解java.util.WeakHashMap的工作原理。我们创建了一个简单的缓存,利用了WeakHashMap的行为,并测试它是否如我们预期的那样工作。

The implementation of all these examples and code snippets can be found in the GitHub project – which is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub项目中找到–它是一个Maven项目,所以应该很容易导入并按原样运行。