引用和对象
每种编程语言都有自己操作内存中元素的的方式,例如在C和C++里是通过指针,而在Java中则是通过"引用"。
在Java中一切都被视为了对象,但是我们操作的标识符实际上是对象的一个引用(reference)。
//创建一个引用,引用可以独立存在,并不一定需要与一个对象关联
String s;
通过将这个叫"引用"的标识符指向某个对象,之后便可以通过这个引用来实现操作对象了。
String s = new String("abc");
System.out.println(s.toString());
在JDK1.2之前,Java中的定义很传统:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。
Java中的垃圾回收机制在判断回收某个对象的时候,都需要依据"引用"这个概念。
在不同垃圾回收算法中,对引用的判断方式有所不同:
- 引用计数法:为每个对象添加一个引用计数器,每当有一个引用指向它时,计数器就加1,当引用失效时,计数器就减1,当计数器为0时,则认为该对象可以被回收
- 可达性分析算法:从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。
JDK1.2之前,一个对象只有"已被引用"和“未被引用”两种状态,这将无法描述某些特殊情况下的对象,比如:当内存充足时需要保留,而内存紧张时才需要被抛弃的一类对象。
因此,在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为了4种,这4种引用的强度依次减弱:
- 强引用(Strong Reference)
- 软引用(Soft Reference)
- 弱引用(Weak Reference)
- 虚引用(Phantom Reference)
1.强引用(Strong Reference)
声明
Java中默认声明的就是强引用,比如:
Object obj = new Object();
强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemoryError也不会回收这种对象。
释放
如果想要中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
比如ArrayList类的remove方法中就是通过将引用赋值为null来实现删除工作的。
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
ArrayList的clear方法也是将内存数组中的每个数组元素都赋值为null
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
因此,clear和remove不代表将数组中的元素减少了,只是将引用赋值为null,数组大小没有改变。
2.软引用(Soft Reference)
软引用时用来描述一些非必须但仍然有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
这种特性常常被用来实现缓存技术,比如网页缓存、图片缓存等。使用软引用能防止内存泄漏,增强程序的健壮性。
在JDK1.2之后,用java.lang.ref.SoftReference类来表示软引用。
SoftReference的特点是它的一个实例保存对一个对象的软引用,该软引用的存在不妨碍垃圾手机线程对该Java对象的回收。
也就是说,一旦SoftReference保存了对一个Java对象的软引用之后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。
当垃圾回收器决定对其回收时,会先清空它的SoftReference,也就是说SoftReference的get方法将会返回null,然后再调动对象的finalize方法,并在下一轮GC中对其真正的进行回收。
声明
//声明s1的强引用
String s1 = new String("abc");
//声明s1的软引用
//此时注意,不能使用s1 = "abc";
//直接赋值是使用的常量池,软引用不起作用
SoftReference<String> softReference = new SoftReference<>(s1);
//释放s1的强引用,此时s1只剩软引用
s1 = null;
//重新获得s1对象的强引用
s1 = softReference.get();
此时,s1对象就成了软引用对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。
也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可及对象会被虚拟机尽可能保留。在回收对象之前,我们可以通过get方法来重新获得对该实例的强引用。而回收之后,再调用get方法就只能得到null了。
注意