Четыре ссылочных типа в Java

Java JVM программист

1. Предпосылки

Освобождение памяти в Java не требует ответственности со стороны программистов, и JVM запускает Java GC для завершения сборки мусора, когда это необходимо. Java предоставляет нам четыре метода ссылок, чтобы мы могли управлять жизненным циклом объектов.Сила ссылок варьируется от сильного к слабому: сильная ссылка, мягкая ссылка, слабая ссылка и виртуальная ссылка.

2. Введение

1. Сильная ссылка StrongReference

StrongReference — это форма ссылки по умолчанию в Java, и при ее использовании не требуется явного определения. Независимо от того, насколько ограничены системные ресурсы для любого объекта, используемого сильной ссылкой, Java GC не будет активно собирать объекты с сильными ссылками.

public class StrongReferenceTest {

	public static int M = 1024*1024;

	public static void printlnMemory(String tag){
		Runtime runtime = Runtime.getRuntime();
		int M = StrongReferenceTest.M;
		System.out.println("\n"+tag+":");
		System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
	}
	
	public static void main(String[] args){
		StrongReferenceTest.printlnMemory("1.原可用内存和总内存");
		
		//实例化10M的数组并与strongReference建立强引用
		byte[] strongReference = new byte[10*StrongReferenceTest.M];
		StrongReferenceTest.printlnMemory("2.实例化10M的数组,并建立强引用");
		System.out.println("strongReference : "+strongReference);
		
		System.gc();
		StrongReferenceTest.printlnMemory("3.GC后");
		System.out.println("strongReference : "+strongReference);

		//strongReference = null;后,强引用断开了
		strongReference = null;
		StrongReferenceTest.printlnMemory("4.强引用断开后");
		System.out.println("strongReference : "+strongReference);
		
		System.gc();
		StrongReferenceTest.printlnMemory("5.GC后");
		System.out.println("strongReference : "+strongReference);
		}
}

результат операции:


2. Слабая ссылка WeakReference

Если объект имеет только слабые ссылки, независимо от того, достаточно ли памяти или нет, после Java GC, если объект имеет только слабые ссылки, он будет автоматически восстановлен.

public class WeakReferenceTest {
	
	public static int M = 1024*1024;
	
	public static void printlnMemory(String tag){
		Runtime runtime = Runtime.getRuntime();
		int M = WeakReferenceTest.M;
		System.out.println("\n"+tag+":");
		System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
	}
	
	public static void main(String[] args){  
		WeakReferenceTest.printlnMemory("1.原可用内存和总内存");

		//创建弱引用
		WeakReference<Object> weakRerference = new WeakReference<Object>(new byte[10*WeakReferenceTest.M]);   
		WeakReferenceTest.printlnMemory("2.实例化10M的数组,并建立弱引用");
		System.out.println("weakRerference.get() : "+weakRerference.get());
		
		System.gc();
		StrongReferenceTest.printlnMemory("3.GC后");
		System.out.println("weakRerference.get() : "+weakRerference.get());
	}   
}

результат операции:


3. Мягкая ссылка SoftReference

Характеристики мягких ссылок и слабых ссылок в основном одинаковы, главное отличие состоит в том, что мягкие ссылки будут перерабатываться только при нехватке памяти. Если объект имеет только программные ссылки, сборщик мусора Java не будет восстанавливать его, когда памяти достаточно, и будет восстанавливать, когда памяти недостаточно.

public class SoftReferenceTest {
	
	public static int M = 1024*1024;
	
	public static void printlnMemory(String tag){
		Runtime runtime = Runtime.getRuntime();
		int M = StrongReferenceTest.M;
		System.out.println("\n"+tag+":");
		System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
	}
	
	public static void main(String[] args){
		SoftReferenceTest.printlnMemory("1.原可用内存和总内存");
		
		//建立软引用
		SoftReference<Object> softRerference = new SoftReference<Object>(new byte[10*SoftReferenceTest.M]);
		SoftReferenceTest.printlnMemory("2.实例化10M的数组,并建立软引用");
		System.out.println("softRerference.get() : "+softRerference.get());
	  
		System.gc();  
		SoftReferenceTest.printlnMemory("3.内存可用容量充足,GC后");
		System.out.println("softRerference.get() : "+softRerference.get());  

		//实例化一个4M的数组,使内存不够用,并建立软引用
		//free=10M=4M+10M-4M,证明内存可用量不足时,GC后byte[10*m]被回收
		SoftReference<Object> softRerference2 = new SoftReference<Object>(new byte[4*SoftReferenceTest.M]);
		SoftReferenceTest.printlnMemory("4.实例化一个4M的数组后");
		System.out.println("softRerference.get() : "+softRerference.get());
		System.out.println("softRerference2.get() : "+softRerference2.get());  
	 } 
}

результат операции:


4. Фантомная ссылка

Из исходного кода класса PhantomReference можно узнать, что его метод get() будет возвращать только null, когда бы он ни был. Поэтому бессмысленно использовать только виртуальные ссылки, и их нужно использовать в связке с классом ReferenceQueue. При выполнении Java GC, если объект имеет только виртуальные ссылки, он добавит объект в связанную с ним ReferenceQueue.

public class PhantomReferenceTest {

	public static int M = 1024*1024;

	public static void printlnMemory(String tag){
		Runtime runtime = Runtime.getRuntime();
		int M = PhantomReferenceTest.M;
		System.out.println("\n"+tag+":");
		System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		PhantomReferenceTest.printlnMemory("1.原可用内存和总内存");
		byte[] object = new byte[10*PhantomReferenceTest.M];		
		PhantomReferenceTest.printlnMemory("2.实例化10M的数组后");
		
	    //建立虚引用
	    ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
	    PhantomReference<Object> phantomReference = new PhantomReference<Object>(object,referenceQueue);  
	    
	    PhantomReferenceTest.printlnMemory("3.建立虚引用后");
	    System.out.println("phantomReference : "+phantomReference); 
	    System.out.println("phantomReference.get() : "+phantomReference.get());
	    System.out.println("referenceQueue.poll() : "+referenceQueue.poll());
	    
	    //断开byte[10*PhantomReferenceTest.M]的强引用
	    object = null;  
	    PhantomReferenceTest.printlnMemory("4.执行object = null;强引用断开后");
	    
	    System.gc();
	    PhantomReferenceTest.printlnMemory("5.GC后");
	    System.out.println("phantomReference : "+phantomReference); 
	    System.out.println("phantomReference.get() : "+phantomReference.get());
	    System.out.println("referenceQueue.poll() : "+referenceQueue.poll());	    
	   
	    //断开虚引用
	    phantomReference = null;
		System.gc(); 
		PhantomReferenceTest.printlnMemory("6.断开虚引用后GC");
	    System.out.println("phantomReference : "+phantomReference);
	    System.out.println("referenceQueue.poll() : "+referenceQueue.poll());	    	
	}
}

результат операции:


3. Резюме

Сильная ссылка — это форма ссылки по умолчанию в Java. Ее не нужно явно определять при использовании. Это наиболее часто используемый метод ссылки. Независимо от того, насколько ограничены системные ресурсы, Java GC не будет активно восстанавливать объекты с сильными ссылками. Слабые ссылки и мягкие ссылки обычно используются, когда объект, на который ссылаются, не требуется. Разница между ними в том, что объекты, связанные со слабыми ссылками, всегда собираются при сборке мусора, а объекты, связанные с мягкими ссылками, собираются только при нехватке памяти. Метод get() виртуальной ссылки всегда получает значение null, и экземпляр объекта не может быть получен. Java GC поместит виртуальные ссылочные объекты в ссылочную очередь. Его можно использовать для дополнительной очистки ресурсов или отката транзакций при повторном использовании объекта. Поскольку экземпляр объекта, на который делается ссылка, не может быть получен из виртуальной ссылки. Его использование весьма специфично, поэтому виртуальный эталон не помещается в таблицу для сравнения. Вот сравнение сильных ссылок, слабых ссылок и мягких ссылок:

тип ссылки Памяти JVM достаточно во время GC JVM не хватает памяти во время GC
сильная цитата не переработанный не переработанный
слабая ссылка Быть переработанным Быть переработанным
мягкая ссылка не переработанный Быть переработанным