本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
引用类型简介
java中的引用其实就像是一个对象的名字或者别名,一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,可以把它想象为类似C++语言中指针的东西,它指示了对象在内存中的地址,只不过我们不能够观察到这个地址究竟是什么。
如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间(stack,栈空间)来保存。但是它们的值是相同的,都指示同一个对象在内存(heap,堆空间)的中位置。在java中引用类型分为两类:值类型和引用类型,其中值类型就是基本数据类型,如int,double类型,而引用类型就是除了基本数据类型之外的所有类型。在JDK.1.2之后Java对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,这4种引用的强度依次减弱。
强引用
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。比如:A a = new A()。强引用有一下三个特性:
强引用可以直接访问目标对象。
强引用所指向的对象在任何时候都不会被系统回收。
强引用可能导致内存泄漏。
源码如下:
1 | scala复制代码/** |
对象新建后默认为强引用类型的,是普遍对象引用的类型。FinalReference的源码只有一个空实现,这也说明强引用是“默认引用类型”
GC 回收问题
对象因为Finalizer的引用而变成了一个临时的强引用,即使没有其他的强引用,还是无法立即被回收;
对象至少经历两次GC才能被回收,因为只有在FinalizerThread执行完了对象的finalize方法的情况下才有可能被下次GC回收,而有可能期间已经经历过多次GC了,但是一直还没执行对象的finalize方法;
CPU资源比较稀缺的情况下FinalizerThread线程有可能因为优先级比较低而延迟执行对象的finalize方法;
因为对象的finalize方法迟迟没有执行,有可能会导致大部分f对象进入到old分代,此时容易引发old分代的GC,甚至Full GC,GC暂停时间明显变长,甚至导致OOM;
软引用
软引用是用来描述一些“还有用但是非必须”的对象。软引用的回收策略在不同的JVM实现会略有不同,JVM不仅仅只会考虑当前内存情况,还会考虑软引用所指向的referent最近的使用情况和创建时间来综合决定是否回收该referent。软引用保存了两个变量:
timestamp:每次调用get方法都会更新时间戳。JVM可以利用该字段来选择要清除的软引用,但不是必须要这样做。
clock:时间锁,由垃圾收集器更新。
因此,任何GC都可以使用这些字段并定义清除软引用的策略,例如:最后清除最近创建的或最近使用的软引用。在JDK 1.2之后,提供了SoftReference类来实现软引用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。源码如下:
1 | csharp复制代码/** |
下面看一个例子:
1 | csharp复制代码/** |
弱引用
用来描述非必须的对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发送之前。开始回收是,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。Java垃圾回收器准备对WeakReference所指向的对象进行回收时,调用对象的finalize()方法之前,WeakReference对象自身会被加入到这个ReferenceQueue对象中,此时可以通过ReferenceQueue的poll()方法取到它们。源码如下:
1 | java复制代码/** |
虚引用
虚引用是所有引用类型中最弱的一种。一个对象是否关联到虚引用,完全不会影响该对象的生命周期,也无法通过虚引用来获取一个对象的实例。为对象设置一个虚引用的唯一目的是:能在此对象被垃圾收集器回收的时候收到一个系统通知,它就是利用ReferenceQueue实现的。当referent被gc回收时,JVM自动把虚引用对象本身加入到ReferenceQueue中,表明该reference指向的referent被回收。然后可以通过去queue中取到reference,可以通过这个来做额外的清理工作。可以用虚引用代替对象finalize方法来实现资源释放,这样更加灵活和安全。
PhantomReference只有当Java垃圾回收器对其所指向的对象真正进行回收时,会将其加入到这个ReferenceQueue对象中,这样就可以追踪对象的销毁情况。这里referent对象的finalize()方法已经调用过了。
所以具体用法和之前两个有所不同,它必须传入一个ReferenceQueue对象。当虚引用所引用对象准备被垃圾回收时,虚引用会被添加到这个队列中。源码如下:
1 | java复制代码/** |
下面看一个例子:
1 | csharp复制代码public class PhantomReferenceTest { |
总结
Java的4种引用的级别由高到低依次为:
强引用 > 软引用 > 弱引用 > 虚引用。
抽奖说明
1.本活动由掘金官方支持 详情可见juejin.cn/post/701221…
2.通过评论和文章有关的内容即可参加,要和文章内容有关哦!
3.本月的文章都会参与抽奖活动,欢迎大家多多互动!
4.除掘金官方抽奖外本人也将送出周边礼物(马克杯一个和掘金徽章若干,马克杯将送给走心评论,徽章随机抽取,数量视评论人数增加)。
本文转载自: 掘金