思考:Java中为何会有引用的概念?
思路:在Java里,当一个对象M被创建的时候,它就被放在heap里。当GC运行时,如果发现没有任何引用指向对象M,M就会被回收,用以腾出内存空间。
总结:如果一个对象被回收,需要满足两个条件:
- 没有任何引用指向它
- 触发GC(Grabage Collection)
众所周知,Java在不主动回收内存方面而优于C、C++等语言所以,有没有什么省心的主动触发GC呢?当然有的--引用。
1、强引用(StrongReference)
定义:强引用是使用最普遍的引用。如果一个对象具有强引用,垃圾回收器绝对不会回收它。
实例代码(1):
public static void main(String[] args){
Object object=new Book();
System.gc();
}
运行结果:
<无任何结果>
说明:由于object对象是强引用,即使调用了垃圾回收,也不会回收object。当内存不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会随意回收具有强引用的对象来解决内存不足的问题。如果不使用时,可以通过如下方式来弱化引用:
object=null; //帮助垃圾回收器回收此对象
实例代码(2):
public void test(){
Object object=new Object();
......
}
说明:在一个方法的内部有一个强引用,这个引用保存在栈中,而真正的引用内容(Object)保存在堆中。当这个方法运行完成后就会退出方法栈,则引用内容的引用不存在,这个Object对象就会被回收。
注意:如果这个object是个全局变量时,就需要在不使用该对象时置为null,因为强引用不会被垃圾回收。
实例代码(3):回收时将数组中的内容置为null,数组对象为强引用。
private Object[] arr;
public void clear(){
//let gc do the work
for(int i=0;i<arr.length;i++){
arr[i]=null;
}
}
说明:arr数组为强引用,调用清空数组的方法将每个数组内容赋值为null。不同于arr=null,arr的强引用仍然存在,避免在后续调用arr的add()等方法时重新分配内存。但是仍然可以将数组中存放的引用类型清空,及时释放内存。
2、软引用(SoftReference)
理解:如果一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足,就会将这些对象的内存回收。只要它没有被GC,该对象就可以被程序使用。
应用之一:用来实现内存敏感的高速缓存
String value=new String("xxx");//强引用
SoftReference<String> softRefence=new SoftReference<String>(value);//软引用
当内存不足时,可以理解为:
while(JVM.内存不足){
value=null; //转为软引用
System.gc();
}
引用二:浏览器的后退按钮的引用(当按后退按钮时,上一个显示的页面内容是重新进行网路请求还是从缓存中获取?)
1、在当前页面浏览结束时,就进行内存回收,那么下次返回到当前页面就需要重新进行网络请求
2、如果在当前页面浏览结束时,将浏览内容放入内存,会造成大量的内存浪费,甚至会造成内存溢出
那么软引用的好处来了:(当浏览的缓存达到一定的容量,就将对象回收)
Browser bro=new Browser(); //获取的浏览内容对象
SoftReference soft=new SoftReference(bro);//在这之前添加逻辑判断,浏览结束后,就将该对象置为软引用
if(null != soft.get()){
soft=(Browser)bro.get();//如果没有被GC,直接复用
}else{
bro=new Browser(); //重新构建,达到了内存复用
soft=new SoftReference(bro);
}
软引用和引用队列(ReferenceQueue)联合使用的情况:如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中(了解即可)。
3、弱引用
弱引用与软引用的区别:具有弱引用的对象拥有更短的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存控件是否足够,都会回收它的内存。由于垃圾回收线程的优先级很低,因此不一定很快发现具有弱引用的对象。
实例代码:
public static void main(String[] args){
String value=new String("xxx");
WeakReference weak=new WeakReferenc(value);
System.gc();
}
}
运行结果:
obj [Date:13722379033165] is gc
结果说明:JVM垃圾回收时,弱引用被回收
运行结果等同于:
String value=new String("xxx");
//垃圾回收
if(JVM.垃圾回收){
value=null;
System.gc();
}
用例:如果这个对象只是偶尔的使用,而你希望在使用时能够随时获取,但又不影响内存的垃圾回收,那么应该使用WeakReference来标记此对象。
下面的代码会让value再次变成强引用:
String str=weak.get();
4、虚引用
虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。(理解即可)
总结:
Java的四种引用的级别由高到低依次为:
强引用-->软引用-->弱引用-->虚引用
当垃圾回收器回收时,某些对象会被回收,某些对象不会被回收。垃圾回收器会从根对象Object来标记存活的对象,然后将某些不可达的对象和一些引用的对象进行回收,通过表格说明一下: