java有V 的引用吗_Java 中的四种引用

之前我们提到过 GC,但当 Java 中引用的对象越来越多,会导致内存空间不足,最终会产生错误 OutOfMemoryError,并让应用程序终止。那为什么 GC 在此时不能多收集一些对象呢?这就和今天说的引用类型有关了。

首先,从 JDK1.2 开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

强引用

强引用(Strong Reference)是使用最普遍的引用。如果一个对象具有强引用,那么它永远不会被 GC。例如:

Object strongReference = new Object();

当内存空间不足时,JVM 宁愿抛出OutOfMemoryError,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

如果强引用对象不使用时,需要弱化从而可以被 GC,例如ArrayList中的clear()方法:

/**

* Removes all of the elements from this list. The list will

* be empty after this call returns.

*/

public void clear() {

modCount++;

// clear to let GC do its work

for (int i = 0; i < size; i++)

elementData[i] = null;

size = 0;

}

显式地设置强引用对象为null,或让其超出对象的生命周期范围,则垃圾回收器认为该对象不存在引用,就会回收这个对象。具体什么时候收集这要取决于具体的垃圾回收器。

软引用

如果一个对象只具有软引用(Soft Reference),当内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。让我们来看一个例子具体了解一下:

String str = new String("abc");

SoftReference softReference = new SoftReference<>(str);

String result = softReference.get();

让我们来看一下get():

public T get() {

T o = super.get();

// timestamp代表上一次软引用上一次被使用的时间(初始化、get())

// clock代表上一次GC的时间

if (o != null && this.timestamp != clock)

this.timestamp = clock;

return o;

}

因此,软引用在被垃圾回收时,也遵循LRU法则,优先回收最近最少被使用的对象进行回收。

软引用的使用场景多是内存敏感的高速缓存。具体来说,就是我们希望将数据存放到缓存中,这样可以快速进行读取。但是,当 JVM 中内存不够用时,我们又不希望缓存数据会占用到 JVM 的内存。例如配合ReferenceQueue,如果软引用所引用对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中:

ReferenceQueue referenceQueue = new ReferenceQueue<>();

String str = new String("abc");

SoftReference softReference = new SoftReference<>(str, referenceQueue);

str = null;

// Notify GC

System.gc();

System.out.println(softReference.get()); // abc

Reference extends String> reference = referenceQueue.poll();

System.out.println(reference); //null

但是需要注意的是,如果使用软引用缓存,有可能导致Full GC增多。

弱引用

如果一个对象只具有弱引用(Weak Reference),其生命周期相比于软引用更加短暂。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会对它进行回收。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。其使用为:

String str = new String("abc");

WeakReference weakReference = new WeakReference<>(str);

str = weakReference.get();

讲到弱引用,就不得不提到WeakHashMap。和HashMap相比,当我们给 JVM 分配的内存不足的时候,HashMap 宁可抛出 OutOfMemoryError 异常,也不会回收其相应的没有被引用的对象,而 WeakHashMap 则会回收存储在其中但有被引用的对象。

WeakHashMap 通过将一些没有被引用的键的值赋值为 null ,这样的话就会告知GC去回收这些存储的值了。假如我们特地传入 key 为 null 的键,WeakHashMap 会将键设置为特殊的 Oject,源码为:

public V put(K key, V value) {

// key会被重新赋值

Object k = maskNull(key);

int h = hash(k);

Entry[] tab = getTable();

int i = indexFor(h, tab.length);

for (Entry e = tab[i]; e != null; e = e.next) {

if (h == e.hash && eq(k, e.get())) {

V oldValue = e.value;

if (value != oldValue)

e.value = value;

return oldValue;

}

}

modCount++;

Entry e = tab[i];

tab[i] = new Entry<>(k, value, queue, h, e);

if (++size >= threshold)

resize(tab.length * 2);

return null;

}

/**

* Value representing null keys inside tables.

* 特殊的key

*/

private static final Object NULL_KEY = new Object();

/**

* Use NULL_KEY for key if it is null.

*/

private static Object maskNull(Object key) {

return (key == null) ? NULL_KEY : key;

}

虚引用

虚引用(PhantomReference),顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。 虚引用与软引用和弱引用的一个区别在于:

虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

例如:

String str = new String("abc");

ReferenceQueue queue = new ReferenceQueue();

// 创建虚引用,要求必须与一个引用队列关联

PhantomReference pr = new PhantomReference(str, queue);

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,也可以理解为一种回调方法。

总结

Java 中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用

通过表格,说明其特性:

引用类型

被垃圾回收的时间

使用场景

生存时间

强引用

从来不会

对象的一般状态

JVM停止运行时

软引用

内存不足时

对象缓存

内存不足时

弱引用

正常垃圾回收时

对象缓存

垃圾回收后终止

虚引用

正常垃圾回收时

跟踪对象的垃圾回收

垃圾回收后终止

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

原文出处:https://www.cnblogs.com/death00/p/11718771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值