参考文章

1 强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。

java程序中,一般由Object object = new Object();定义的object就是一个强引用,也就是说object强引用new Object()对象。这种情况下,也就是说明,该对象是程序中必不可少的一部分,当程序遇到内存不足时,jvm并不会将其释放,而是会抛出一个OutOfMemoryError异常。如下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
public class StackDemo {
private static int _10M = 1014 * 1024 * 10;//这是一个10M的字节空间
private int i = 0;

public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
for (int i = 0;i < 4; i++){
list.add(new byte[_10M]);
}
}
}

由代码可以看出,list是一个对new ArrayList<>()的 强引用,所以在遇到内存不足时,就抛出了下面的异常:

1.1 什么情况下,强引用会被垃圾回收掉?

当java程序中所有的引用对象都断开了对该强引用的引用,那么JVM就认为该对象没有使用价值了,也就被垃圾回收掉了。

2 软引用(SoftReference)

如果一个java对象只具有软引用,那么当内存充足时,java的垃圾回收机制不会回收当前对象;然而,当内存不足时,java垃圾回收机制会回收当前的软引用对象。只要当前软引用没有被垃圾回收掉,那么java程序就可以调用该软引用对象。

  • 软引用和强引用的区别:
    • 当该软引用对象没有被强引用对象直接引用时,那么当前内存不足时,软引用引用的对象就会被JVM垃圾回收掉。
    • 强引用之后当所有的gc root对象没有引用当前强引用对象时,且当内存不足时,才会被垃圾回收掉。
  • 软引用和弱引用的区别:
    • 弱引用被垃圾回收的条件相对软引用更加宽松,也就是说,只要发生了垃圾回收,那么不管当前内存是否充足,弱引用对象都会被垃圾回收掉。

看下面这段软引用代码了解一下软引用的具体应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StackDemo {
private static int _10M = 1014 * 1024 * 10;//这是一个10M的字节空间

public static void main(String[] args) {
soft();
}

public static void soft() {
SoftReference<byte[]> softReference;
ReferenceQueue<SoftReference> referenceQueue;
List<SoftReference> list = new ArrayList<>();
System.out.println("------当前为软引用------------");
for (int i = 0;i < 4; i++){
softReference = new SoftReference<>(new byte[_10M]);
list.add(softReference);
System.out.println(list.size());
}
System.out.println("----------下面是输出-------------");
for (SoftReference<byte[]> ref : list ){
System.out.println(ref.get());
}
}
}

程序运行结果如下,并没有像强引用那样,报一个内存不足的错误:

软引用运行结果

从运行结果可以看出,使用软引用去引用10M的字节块,程序没有出错,但是可以看出,前三个块都没系统释放了,也就是说当成垃圾给回收掉了,只有最后一个被保留了下来。使用这种方式,可以使得系统在引用一些资源时,即使资源过多,也不会导致程序崩溃。

软引用使用场景:

  • 当我们需要调用一些图片信息等资源时,可以使用这种引用方式,因为这不是程序所必须的资源,当需要时,可以在临时加载,这样就大大节约了系统的资源。

3 弱引用(WeakReference)

计算机程序设计中,弱引用与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。一些配有垃圾回收机制的语言,如JavaC#PythonPerlLisp等都在不同程度上支持弱引用。

from wikipedia

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

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StackDemo {
private static int _10M = 1014 * 1024 * 10;//这是一个10M的字节空间
private int i = 0;

public static void main(String[] args) {
WeakReference<byte[]> bytes;
List<WeakReference> list = new ArrayList<>();
for (int i = 0;i < 4; i++){
bytes = new WeakReference<byte[]>(new byte[_10M]);
list.add(bytes);
}
}
}

程序运行没有报错:

引用队列

由于软引用和弱引用本身也会占用一定的内存,所以当软引用或者弱引用被垃圾回收掉之后,JVM会将软引用本身或者弱引用本身放到引用队列中,当我们需要释放这一段内存时,需要遍历引用队列中的所有对象并将其释放。

软引用和弱引用都可以配合使用引用队列来释放引用自身所占用的内存。

代码示例:这里还是以上面的软引用为例,但是这里配合了引用队列,将已经回收掉的软引用本身释放掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ReferenceDemo {
private static int _10M = 1014 * 1024 * 10;//这是一个10M的字节空间
//堆内存大小设置为30m -Xmx30m
public static void main(String[] args) {
try {
soft();
}catch (Exception e){
System.out.println(e);
}

}

public static void soft() {
SoftReference<byte[]> softReference;
ReferenceQueue<SoftReference> referenceQueue;
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

List<SoftReference> list = new ArrayList<>();
System.out.println("------当前为软引用------------");
for (int i = 0;i < 4; i++){
//当软引用被垃圾回收时,当前软引用就会被加入到引用队列中去
byte[] bytes = new byte[_10M];
System.out.println(bytes);
softReference = new SoftReference<>(bytes, queue);
list.add(softReference);
}
//释放引用队列中的软引用对象
Reference ref0 = queue.poll();
while (ref0 != null){
list.remove(ref0);
ref0 = queue.poll();
}

System.out.println("----------下面是输出-------------");
for (SoftReference<byte[]> ref : list ){
System.out.println(ref.get());
}
}
}

引用队列运行结果

从运行结果可以看出,前面的三个null对象已经被释放掉了,就剩下最后一个。弱引用配合引用队列和软引用类似。

4 虚引用(PhantomReference)

虚引用,其前几种引用不同,当一个对象被虚引用时,那么说明该对象,在任何时候都可以被垃圾回收,不需要考虑当前内存是否充足。且虚引用必须配合引用队列才能使用。

虚引用和软引用、弱引用的区别在于,虚引用和终接器引用在使用时需要关联一个引用队列(ReferenceQueue)才能使用,而软引用和弱引用则不需要。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ReferenceDemo {
private static int _10M = 1014 * 1024 * 10;//这是一个10M的字节空间
//堆内存大小设置为30m -Xmx30m
public static void main(String[] args) {
try {
phantom();
}catch (Exception e){
System.out.println(e);
}

}

public static void phantom(){
ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
PhantomReference<byte[]> phantomReference;
List<PhantomReference> list = new ArrayList<>();
System.out.println("------当前为虚引用------------");
for (int i = 0;i < 4; i++){
phantomReference = new PhantomReference<>(new byte[_10M],referenceQueue);
list.add(phantomReference);
System.out.println(list);
}
Reference reference = referenceQueue.poll();
while (reference != null){
System.out.println(reference.get());
reference = referenceQueue.poll();
}
}
}

运行结果

疑问?

这里一个小疑问,就是我用的虚引用去引用对象,但是报错了,说堆内存溢出,我的堆内存设置的30M,软引用都会自动回收内存。

5 终接器引用

写在最后

欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。
微信公众号