JVM之引用

JVM之引用

Java 中有四种引用类型:强引用、软引用、弱引用、虚引用。

强引用

强引用是最普遍的一种引用,我们写的代码,99.9999% 都是强引用:

要某个对象有强引用与之关联,这个对象永远不会被回收,即使内存不足,JVM 宁愿抛出 OOM,也不会去回收。

软引用(SoftReference)

All soft references to softly-reachable objects are guaranteed to be cleared before a JVM throws an OutOfMemoryError.

No guarantees are placed upon the time when a soft reference gets cleared or the order in which a set of such references to different objects get cleared. JVM implementations choose between cleaning of either recently-created or recently-used references.

In comparison to weak references, soft references can have longer lifetimes since they continue to exist until extra memory is required. Therefore, they're a better choice if we need to hold objects in memory as long as possible.

Soft references can be used for implementing memory-sensitive caches where memory management is a very important factor.As long as the referent of a soft reference is strongly reachable, that is – is actually in use, the reference won't be cleared. A cache can, for example, prevent its most recently used entries from being discarded by keeping strong referents to those entries, leaving the remaining entries to be discarded at the discretion of the Garbage Collector.

Eg:


SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student())

当内存不足,会触发 JVM 的 GC,如果 GC 后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM 才会回收该对象。

使用场景: 内存敏感的缓存

弱引用(WeakReference)

弱引用的特点是不管内存是否足够,只要发生 GC,都会被回收.

A weakly referenced object is cleared by the Garbage Collector when it's weakly reachable.

Weak reachability means that an object has neither strong nor soft references pointing to it.

Weak vs Soft References

Soft references are basically a big LRU cache. That is, we use soft references when the referent has a good chance of being reused in the near future.

A soft reference may be available for minutes or even hours after the referent becomes unreachable. On the other hand, a weak reference will be available only for as long as its referent is still around.

Eg:

Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();

WeakReference weakReference1 = new WeakReference<>(referent);
WeakReference weakReference2 = new WeakReference<>(referent, referenceQueue);

most known use of these references is the *WeakHashMap* class.

WeakHashMap

WeakHashMap as an Efficient Memory Cache

eg: we want to build a cache that keeps big image objects as values, and image names as keys

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.

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. - by setting value for key to null

虚引用 (PhantomReference);

eg

ReferenceQueue queue = new ReferenceQueue();
PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);
System.out.println(reference.get());

//调用get,输出null

虚引用特点之一了:无法通过虚引用来获取对一个对象的真实引用。

虚引用必须与 ReferenceQueue 一起使用,当 GC 准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的 ReferenceQueue 中。

Phantom references are most often used to schedule post-mortem cleanup actions.

two common use-cases:

  1. determine when an object was removed from the memory which helps to schedule memory-sensitive tasks. For example, we can wait for a large object to be removed before loading another one.
  2. avoid using the *finalize* method and improve the** finalization process.

example

public class LargeObjectFinalizer extends PhantomReference<Object> {

public LargeObjectFinalizer(
Object referent, ReferenceQueue<? super Object> q) {
super(referent, q);
}

public void finalizeResources() {
// free resources
System.out.println("clearing ...");
}
}
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
List<LargeObjectFinalizer> references = new ArrayList<>();
List<Object> largeObjects = new ArrayList<>();

for (int i = 0; i < 10; ++i) {
Object largeObject = new Object();
largeObjects.add(largeObject);
references.add(new LargeObjectFinalizer(largeObject, referenceQueue));
}

largeObjects = null;
System.gc();

Reference<?> referenceFromQueue;
for (PhantomReference<Object> reference : references) {
System.out.println(reference.isEnqueued());
}

while ((referenceFromQueue = referenceQueue.poll()) != null) {
((LargeObjectFinalizer)referenceFromQueue).finalizeResources();
referenceFromQueue.clear();
}
  1. referenceQueue* - to keep track of enqueued references
  2. creating these objects using the Object and LargeObjectFinalizer classes.
  3. manually free up a large piece of data by dereferencing the largeObjects list, call GC (System.gc()* isn't triggering garbage collection immediately – it's simply a hint for JVM to trigger the process)
  4. for loop demonstrates how to make sure that all references are enqueued – it will print out true for each reference.
  5. e used a while loop to poll out the enqueued references and do cleaning work for each of them.

Finilization & Phantom References

Memory management is done automatically in Java, One downside to this approach is that the programmer cannot know when a particular object will be collected.

problems with finalization

To perform some postmortem cleanup on objects that garbage collector consider as unreachable, one can use finalization. This feature can be utilized to reclaim native resources associated with an object. However, finalizers have many problems associated.

  • Firstly, we can’t foresee the call of finalize().
  • Secondly, Finalization can slowdown an application

You should also use finalization only when it is absolutely necessary. Finalization is a nondeterministic -- and sometimes unpredictable -- process. The less you rely on it, the smaller the impact it will have on the JVM and your application

there is a severe performance penalty for using finalizers... So what should you do instead of writing a finalizer for a class whose objects encapsulate resources that require termination, such as files or threads? Just provide an explicit termination method, and require clients of the class to invoke this method on each instance when it is no longer needed.

An object is phantom reachable if it is neither strongly nor softly nor weakly reachable and has been finalized and there is a path from the roots to it that contains at least one phantom reference.

  • referent - the object the new phantom reference will refer to
  • q - the reference is registered with the given queue.

Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.

Phantom references are safe way to know an object has been removed from memory.

Reference Object GC

Soft Reference can be garbage collected after there are no strong references to the referent. However, it typically retained until memory is low. All softly reachable objects will be reclaimed before an OutOfMemoryException is thrown. Therefore, it can be used to implement caches of objects that can be recreated if needed.

Weak Reference can be garbage collected when there are no strong or soft references to the referent. However, unlike Soft Reference, they are garbage collected on a gc even when memory is abundant. They often can be used for “canonical mappings” where each object has a unique identifier (one-to-one), and in collections of “listeners”

On the other hand, Phantom Reference, can be garbage collected once there are no strong, soft or weak references to the referent. When object is phantomly reachable, it means the object is already finalized but not yet reclaimed, so the GC enqueues it in a ReferenceQueue for post-finalization processing. A PhantomReference is not automatically cleared when it is enqueued, so when we remove a PhantomReference from a ReferenceQueue, we must call its clear() method or allow the PhantomReference object itself to be garbage-collected.

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.

ReferenceQueue

用队列 ReferenceQueue 是用来配合引用工作的,没有 ReferenceQueue 一样可以运行。创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队列的队列末尾,这相当于是一种通知机制。当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVM允许我们在对象被销毁后,做一些我们自己想做的事情。


参考

https://mp.weixin.qq.com/s/psv1Owa2-uAY1itCeFWaPQ

https://mp.weixin.qq.com/s/Hb1VeYh2PILfMbyC1wAzfQ

https://mp.weixin.qq.com/s/-6HztJNO79dD9z7hyLBhlA

https://mp.weixin.qq.com/s/LIoO9ZDkd43G3P3Aa4n0pg

https://www.baeldung.com/java-soft-references

https://www.baeldung.com/java-weak-reference

https://www.baeldung.com/java-phantom-reference

https://dzone.com/articles/finalization-and-phantom