若是一个内存中的对象没有任何引用的话,就说明这个对象已经再也不被使用了,从而能够成为被垃圾回收的候选。不过,因为垃圾回收器的运行时间不肯定,因此可被垃圾回收的对象的实际被回收时间是不肯定的。对于一个对象来讲,只要有引用的存在,它就会一直存在于内存中。若是这样的对象愈来愈多,超出了JVM中的内存总数,JVM就会抛出OutOfMemory错误。java
虽然垃圾回收的具体运行是由JVM来控制的,可是开发人员仍然能够在必定程度上与垃圾回收器进行交互,其目的在于更好的帮助垃圾回收器管理好应用的内存。数组
在Java中把对象的引用分为 4 种级别,从而使程序能更加灵活地控制对象的生命周期。这 4 种级别由高到低依次为:强引用、软引用、弱引用和虚引用。缓存
JAVA对象引用类层次结构图以下图所示:网络
图14-1 JAVA对象引用类层次结构图编辑器
1)强引用类型函数
在通常的Java程序中,见到最多的就是强引用(strong reference)。如咱们最多见的代码this
[代码]java代码:
2 |
String str = “Hello Wolrd”; |
3 |
Object obj = new Object(); |
4 |
Date date = new Date(), |
在上面的代码中str、obj、date都是对象的强引用。对象的强引用能够在程序中处处传递。不少状况下,会同时有多个引用指向同一个对象。强引用的存在限制了对象在内存中的存活时间。假如对象A中包含了一个对象B的强引用,那么通常状况下,对象B的存活时间就不会短于对象A。若是对象A没有显式的把对象B的引用设为null的话,就只有当对象A被垃圾回收以后,对象B才再也不有引用指向它,才可能得到被垃圾回收的机会。url
2)软引用类型spa
软引用(soft reference)在强度上弱于强引用,经过SoftReference类来表示。它的做用是告诉垃圾回收器,程序中的哪些对象并不那么重要,当内存不足的时候是能够被暂时回收的。当JVM中的内存不足的时候,垃圾回收器会释放那些只被软引用所指向的对象。若是所有释放完这些对象以后,内存还不足,才会抛出Out Of Memory错误。设计
软引用很是适合于建立缓存。当系统内存不足的时候,缓存中的内容是能够被释放的。好比考虑一个图像编辑器的程序,该程序会把图像文件的所有内容都读取到内存中,以方便进行处理,而用户也能够同时打开多个文件。当同时打开的文件过多的时候,就可能形成内存不足。若是使用软引用来指向图像文件内容的话,垃圾回收器就能够在必要的时候回收掉这些内存。
从下面的Java代码中能够看到软引用类型的使用方法。
[代码]java代码:
01 |
public class BitmapCache { |
02 |
private String url; //图片URL |
03 |
private SoftReference<Bitmap> softRef; // //软引用-只有当系统内存不足的时候才去释放 |
04 |
public BitmapCache (String url) { |
06 |
softRef = new SoftReference< Bitmap >( null ); |
08 |
private Bitmap loadRemoteBitmap() { |
09 |
final DefaultHttpClient client = new DefaultHttpClient(); |
10 |
final HttpGet getRequest = new HttpGet(url); |
11 |
HttpResponse response = client.execute(getRequest); |
12 |
final int statusCode = response.getStatusLine().getStatusCode(); |
13 |
final HttpEntity entity = response.getEntity(); |
14 |
InputStream inputStream = entity.getContent(); |
15 |
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); |
16 |
OutputStream outputStream = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); |
17 |
copy(inputStream, outputStream); |
19 |
final byte [] data = dataStream.toByteArray(); |
20 |
final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); |
23 |
public Bitmap get Bitmap () { |
24 |
Bitmap bitmap = softRef.get(); |
25 |
if (bitmap == null ) { //系统内存不足的时,图片已经被释放须要从新加载网络图片 |
26 |
bitmap = loadRemoteBitmap (); |
27 |
softRef = new SoftReference< Bitmap >( bitmap); |
在使用上面程序的时候,因为软引用所指向的对象可能被回收掉,在经过get方法来获取软引用所实际指向的对象的时候,老是要检查该对象是否还存活。
3)弱引用类型
弱引用(weak reference)在强度上弱于软引用,经过WeakReference类来表示。它的做用是引用一个对象,可是并不阻止该对象被回收。若是使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被回收的,弱引用则没有这个问题。在垃圾回收器运行的时候,若是一个对象的全部引用都是弱引用的话,该对象会被回收。弱引用的做用在于解决强引用所带来的对象之间在存活时间上的耦合关系。
弱引用最多见的用处是在集合类中,尤为在哈希表中。哈希表的接口容许使用任何Java对象做为键(Key)。当一个键值对(Key-Value)被放入到哈希表中以后,哈希表对象自己就有了对这些键和值对象的引用。若是这种引用是强引用的话,那么只要哈希表对象自己还存活,其中所包含的键和值对象是不会被回收的。若是某个存活时间很长的哈希表中包含的键值对不少,最终就有可能消耗掉JVM中所有的内存。对于这种状况的解决办法就是使用弱引用来引用这些对象,这样哈希表中的键和值对象都能被垃圾回收器及时回收。在Java中可使用WeakHashMap类来知足这一常见需求。
4)虚引用类型
在介绍虚引用以前,先要了解一下Java提供的对象终止化机制(finalization)。
在Object类里面有个finalize方法,其设计的初衷是在一个对象被真正回收以前,能够用来执行一些清理的工做。由于Java并无提供相似C++的析构函数同样的机制,只是简单地经过 finalize方法来实现。可是问题在于垃圾回收器的运行时间是不固定的,因此这些清理工做的实际运行时间也是不能预知的。
使用虚引用(phantom reference)能够解决这个问题。在建立虚引用PhantomReference的时候必需要指定一个引用队列。当一个对象的finalize方法已经被调用了以后,这个对象的虚引用会被加入到队列中。经过检查该队列里面的内容就知道一个对象是否是已经准备要被回收了。
虚引用及其队列的使用状况并很少见,主要用来实现比较精细的内存使用控制,这对于移动设备来讲是颇有意义的。程序能够在肯定一个对象要被回收以后,再申请内存建立新的对象。经过这种方式可使得程序所消耗的内存维持在一个相对较低的数量。
好比下面的Java代码给出了一个缓冲区的实现示例。
[代码]java代码:
02 |
public class PhantomBuffer { |
04 |
private byte [] data = new byte [ 0 ]; |
06 |
private ReferenceQueue< byte []> queue = new ReferenceQueue< byte []>(); |
08 |
private PhantomReference< byte []> ref = new PhantomReference< byte []>(data, queue); |
10 |
public byte [] get( int size) { |
14 |
throw new IllegalArgumentException( "Wrong buffer size" ); |
16 |
//检查当前缓冲区是否能知足申请的缓冲区大小 |
17 |
if (data.length < size) { |
28 |
data = new byte [size]; |
30 |
ref = new PhantomReference< byte []>(data, queue); |
31 |
} catch (InterruptedException e) { |
在上面的代码中,每次申请新的缓冲区的时候,都首先确保以前的缓冲区的字节数组已经被成功回收。引用队列的remove方法会阻塞直到新的虚引用被加入到队列中。须要注意的是,这种作法会致使垃圾回收器被运行的次数过多,可能会形成程序的吞吐量太低。
5)引用队列
在有些状况下,程序会须要在一个对象的可达到性发生变化的时候获得通知。好比某个对象的强引用都已经不存在了,只剩下软引用或是弱引用。可是还须要对引用自己作一些的处理。典型的情景是在哈希表中,引用对象是做为WeakHashMap中的键对象的,当其引用的实际对象被垃圾回收以后,就须要把该键值对从哈希表中删除。有了引用队列(ReferenceQueue),就能够方便的获取到这些弱引用对象,将它们从表中删除。在软引用和弱引用对象被添加到队列以前,其对实际对象的引用会被自动清空。经过引用队列的poll/remove方法就能够分别以非阻塞和阻塞的方式获取队列中的引用对象。