Java 四种引用类型
引用与对象
每种编程语言都有自己操作内容中元素的形式,例如C和C++通过指针,Java中则通过"引用"。Java中一切皆对象,但我们操作的标识符实际上是对象的一个引用"reference".
// 创建一个引用,引用可以独立存在,并不一定要与一个对象关联
String s;
// 引用的标识符指向某个对象,可以通过这个引用来操作对象了。
String str = new String("xyz");
在JDK1.2之前,Java中定义:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,这块内存代表一个引用。一个对象只有"未引用"和"已引用"两个状态,这无法描述某些特殊情况下的对象,比如,内容充足时需要保留,而内存紧张时才需要被抛弃的一类对象。
Java中的GC回收机制在判断是否回收某个对象的时候,都需要依据这个"引用"的概念。引用类型
不同的GC算法判断引用的方式不同:
- 引用计数法(Reference Count):为对象添加一个引用计数器,有引用指向它则计数器+1;引用失效,计数器-1;当引用计数为0时,则该对象可以被GC回收,但是该算法无法解决对象之间循环引用的问题(目前Java已放弃这种方式)。
- 可达性分析法(GC Roots):从GC Roots的对象开始搜索,如果一个对象到GC Roots没有任何引用链接时,说明此对象不可用,可以被GC回收。同时解决了引用计数算法中的循环引用的问题。
JDK1.2以后,Java对引用的概念进行了扩充,分为:强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference)和虚引用(Phantom Reference)四种。四种引用强度依次减弱。1. 强引用 (Strong Reference) Java中默认使用的就是强引用
// 创建一个Object对象,且使用 o 引用指向该对象
// 只要 o 还指向Object对象,就不会被GC回收,即使内存不足时,抛OOM异常也不会被回收。
Object o = new Object();
// 制成null,下次GC就会回收
o = null;
2. 软引用(Soft Reference) 软引用是用来描述一些非必需但仍有用的对象。
内存足够时,软引用不会被回收;只有内存不足时,GC才会回收软引用的对象;如果回收后仍然没有足够内存时,才会抛OOM异常。Java中用 java.lang.ref.SoftReference 类表示软引用
使用场景:缓存技术,包括JVM缓存,图片缓存等
软引用与强引用对比 JVM启动参数:-Xms20m -Xmx20m
强引用示例:
public class StrongReference {
public static void main(String[] args) {
allocateStrongReference();
}private static void allocateStrongReference() {
byte[] buffer = new byte[1024 * 1024 * 10];
}
}
说明:
- buffer 为 10M时,程序运行正常
- buffer 为 20M时,程序抛OOM异常:java.lang.OutOfMemoryError: Java heap space
public class SoftReferenceTest {private static List> list = new ArrayList<>();
public static void main(String[] args) {
testSoftReference();
}private static void testSoftReference() {
// 创建10个大小为2M的字节数组
for (int i = 0;
i < 10;
i++) {
byte[] buffer = new byte[1024 * 1024 * 2];
SoftReference sr = new SoftReference<>(buffer);
list.add(sr);
}// 手动通知GC
System.gc();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}// 打印软引用对象
list.forEach(sr -> System.out.print(sr + " "));
System.out.println("----------------");
// 打印软引用中的对象
list.forEach(sr -> System.out.print(sr.get() + " "));
}
}output:
java.lang.ref.SoftReference@6e1b77 java.lang.ref.SoftReference@1ca772d java.lang.ref.SoftReference@1807454 java.lang.ref.SoftReference@1494fbf java.lang.ref.SoftReference@1dc4ec2 java.lang.ref.SoftReference@133314b java.lang.ref.SoftReference@11bc7ed java.lang.ref.SoftReference@d84586 java.lang.ref.SoftReference@10dae81 java.lang.ref.SoftReference@12c6ec2 java.lang.ref.SoftReference@dde6e5 java.lang.ref.SoftReference@177ecd java.lang.ref.SoftReference@80bfe8 java.lang.ref.SoftReference@a29884 java.lang.ref.SoftReference@169b07b java.lang.ref.SoftReference@c34f4d java.lang.ref.SoftReference@1a7cec2 java.lang.ref.SoftReference@1b3120a java.lang.ref.SoftReference@1539caf java.lang.ref.SoftReference@1fc0f2f
----------------
null null null null null null null null null null null null null null null null [B@1da6444 [B@9f23b4 [B@183da3f [B@18fd1ac
说明:
- 无论创建多少个软引用对象,只有最后几个对象被保留,说明内存不足时软引用的对象会被GC回收
- 即使有 byte[] buffer 引用指向对象, 且 buffer 是一个strong reference, 但是 SoftReference sr 指向的对象仍然被回收了,这是因为Java的编译器发现了在之后的代码中, buffer 已经没有被使用了, 所以自动进行了优化。
弱引用的引用强度比软引用更弱一点。无论内存是否足够,只要JVM进行GC,弱引用关联的对象都会被回收。Java 中使用 java.lang.ref.WeakReference 来表示 弱引用。
示例如下:
public class WeakReferenceTest {private static List list = new ArrayList<>();
public static void main(String[] args) {
testWeakReference();
}private static void testWeakReference() {
for (int i = 0;
i < 10;
i++) {
byte[] buff = new byte[1024 * 1024];
WeakReference sr = new WeakReference<>(buff);
list.add(sr);
}System.gc();
// 打印弱引用中的对象
list.forEach(sr -> System.out.print(sr.get() + " "));
}
}output:
null null null null null null null null null null
弱引用关联的对象都被GC回收了。
4. 虚引用(PhantomReference)
虚引用是最弱的一种引用关系,如果一个对象持有虚引用,那么它就和没有引用一样,它随时可能会被回收。Java中使用 java.lang.ref.PhantomReference 类表示。
源码:
package java.lang.ref;
/**
* 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.
*
* If the garbage collector determines at a certain point in time that the
* referent of a phantom reference is phantom reachable, then at that
* time or at some later time it will enqueue the reference.
*
* In order to ensure that a reclaimable object remains so, the referent of
* a phantom reference may not be retrieved: The get
method of a
* phantom reference always returns null
.
*
* 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.
*
* @authorMark Reinhold
* @since1.2
*/public class PhantomReference extends Reference {/**
* Returns this reference object's referent.Because the referent of a
* phantom reference is always inaccessible, this method always returns
* null
.
*
* @returnnull
*/
public T get() {
return null;
}/**
* Creates a new phantom reference that refers to the given object and
* is registered with the given queue.
*
* It is possible to create a phantom reference with a null
* queue, but such a reference is completely useless: Its get
* method will always return null and, since it does not have a queue, it
* will never be enqueued.
*
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be registered,
*or null if registration is not required
*/
public PhantomReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
}}
通过源码得知
- 只有一个构造方法和get() 方法
- get() 方法返回的是null。说明无法通过虚引用来获取对象。
- 虚引用必须要和ReferenceQueue 引用队列一起使用。
代码示例:
public class PhantomReferenceTest {public static void main(String[] args) throws Exception {
ReferenceQueue queue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference<>(new Person("zhangsan"), queue);
// 无论何时都为null
System.out.println(phantomReference.get());
System.gc();
TimeUnit.SECONDS.sleep(1);
// 无论何时都为null
System.out.println(phantomReference.get());
}}class Person {
private String name;
public Person(String name) {
this.name = name;
}@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Person: " + this + ", is gc");
}
}
5. 引用队列
【Java|Java四种引用类型】引用队列可以与软引用、弱引用以及虚引用一起使用,当GC准备回收对象时,如发现它还有引用,就会在回收对象之前,将这个引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已有引用,来判断被引用的对象是否要被GC回收。6 总结
- 强引用:创建一个Object对象,只要o引用指向这个对象,就不会被GC回收,即使内存不足时,抛OOM异常也不会被回收。
- 软引用:内存足够时,软引用不会被回收;只有内存不足时,GC才会回收软引用的对象;如果回收后仍然没有足够内存时,才会抛OOM异常
- 弱引用:无论内存是否足够,只要JVM进行GC,弱引用关联的对象都会被回收
- 虚引用:最弱的一种引用关系,如果一个对象持有虚引用,那么它就和没有引用一样,它随时可能会被回收
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)