JAVA中的四种引用方式详细介绍

四种引用方式(Reference)分别是:

1、强引用
2、软引用(SoftReference)
3、弱引用(WeakReference)
4、虚引用(PhantomReference)

Java中提供这四种引用类型主要有两个目的:

  • 第一是可以让程序员通过代码的方式决定某些对象的生命周期;
  • 第二是有利于JVM进行垃圾回收。

四种类型引用的概念:

1.强引用
是指创建一个对象并把这个对象赋给一个引用变量。

比如:
int age = 18;
String name = “Tom”;
int[ ] array = new int[5];
代码测试:先定义一个Container类

class Container{
	private Object[] array;
	public Container(int cap) {
		this.array=new Container[cap];
	}
	//在GC回收之前执行--用于检测GC是否启动
	@Override
	protected void finalize() throws Throwable {
		System.out.println("==finalize()==");
	}
}

在定义一个类通过强引用引用Container类

public class TestGC_02 {
    public static void main(String[] args) {
    	doMethod01();
	}	
	private static void doMethod01() {
		//1.强引用
    	Container c1=new Container(100);//c1就是强引用
    	//c1=null;//此时c1指向的Container对象不可达(也就是说JVM访问不到了)    	
    	System.out.println(c1);
    	
    	//手动GC
    	System.gc();//GC启动以后,GC系统会对内存中的对象进行可达性分析。访问不到则进行标记。
   } 	
}

输出结果:
JAVA中的四种引用方式详细介绍_第1张图片
finalize()方法没有执行,说明对象没有被回收。
如果在这个类下创建一个集合,并不断的放数据导致内存溢出,会发生什么呢?
代码示例:

	//自动GC(通过JVM参数进行分析)
	List<byte[]> list=new ArrayList<>();
    	for(int i=0;i<100000;i++) {
    		list.add(new byte[1024*1024]);
    	}

输出结果:
JAVA中的四种引用方式详细介绍_第2张图片
可以看出,尽管内存溢出,但是finalize()方法没有执行,说明对象还是没有被回收。

强引用引用的对象,生命力最强。(对象不会被GC)。
执行Container c1=new Container(100);这句时,如果内存不足,JVM会抛出内存溢出(OutOfMemory)错误,但不会回收Container指向的对象。

不过要注意的是,如果Container没有引用对象了,那么doMethod01方法执行完之后,c1才会被回收,否则,只要有引用对象都不会回收。

代码示例:
把上面代码中的c1=null的注释放开就可以了,这里就不再插入了,

执行结果:
JAVA中的四种引用方式详细介绍_第3张图片
finalize()方法执行,说明对象被回收。所以如果想中断强引用和某个对象之间的关联,可以显示地将c1赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

2.软引用(SoftReference)

软引用引用的对象,在内存不足时可能会被GC,在内存充足的情况下是不会被GC的。只要垃圾回收器没有回收它,该对象就可以被程序使用。

软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。

public class TestGC_02 {

    public static void main(String[] args) {
    	doMethod02();
	}
	
	private static void doMethod02() {
    	//2.软引用
    	SoftReference<Container> c2=new SoftReference<Container>(new Container(20));
    	Container cc=c2.get();//这种写发是又将软引用转换为了强引用。不推荐使用
    	System.out.println(c2.get());//通过软引用get方法获取和操作引用对象
    	
    	//手动GC
    	System.gc();//GC启动以后,GC系统会对内存中的对象进行可达性分析。访问不到则进行标记。 
	}
}

输出结果:
JAVA中的四种引用方式详细介绍_第4张图片
finalize()方法没有执行,说明对象没有被回收。
如果我们同时在这个类下创建一个集合,一致往集合里放数据,使其达到内存溢出,测试对象是否会被回收?

		//自动GC(通过JVM参数进行分析)
    	List<byte[]> list=new ArrayList<>();
    	for(int i=0;i<100000;i++) {//这地方尽量写大一点,否则计算机内存比较大则也不会内存溢出
    		list.add(new byte[1024*1024]);
    	}

输出结果:
JAVA中的四种引用方式详细介绍_第5张图片
finalize()方法执行,说明对象被回收。并且报了一个OOM异常,说明软引用在内存溢出时可能会被回收,注意:内存溢出不代表内存满了,而是达到一个临界值。

垃圾收集线程会在虚拟机抛出OutOfMemoryError之前。回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用的软引用对象,对那些刚刚构建的或刚刚使用过的“新”软引用对象会被虚拟机尽可能保留。

3.弱引用(WeakReference)

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:

public class TestGC_02 {

    public static void main(String[] args) {
    	doMethod03();
	}
	
	private static void doMethod03() {
    	//3.弱引用
    	WeakReference<Container> c3=new WeakReference<Container>(new Container(100));
    	System.out.println(c3.get());
    	
    	//手动GC
    	System.gc();//GC启动以后,GC系统会对内存中的对象进行可达性分析。访问不到则进行标记。
	}
}

输出结果:
JAVA中的四种引用方式详细介绍_第6张图片
finalize()方法执行,说明对象被回收。

4.虚引用(PhantomReference)

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
总结:
软引用和弱引用
对于强引用,我们平时在编写代码时经常会用到。而对于其他三种类型的引用,使用得最多的就是软引用和弱引用,这2种既有相似之处又有区别。它们都是用来描述非必需对象的,但是被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。

在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。

如何利用软引用和弱引用解决OOM(OutOfMemory)问题?

举个例子,假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。

设计思路是:用一个HashMap来保存图片的路径 和 相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。在Android开发中对于大量图片下载会经常用到。

你可能感兴趣的:(Java,JVM)