强引用(默认是该引用StrongReference)
是最常见的普通对象引用,只要还有强引用指向一个对象,就表明对象还活着,垃圾收集器不会碰这种对象,即使该对象以后永远都不会被用到JVM也不会回收
因此强引用是造成Java内存泄漏的主要原因之一
若将强引用显式地将引用赋值为null
或超过了引用的作用域
,就可以被垃圾收集了
代码Demo
public class StrongReferenceDemo{
public static void main(String[] args){
Object obj1=new Object();//这样定义的默认就是强引用
Object obj2=obj1;//obj2引用赋值,则它也是强引用
obj1=null;//置空
System.gc();//垃圾回收
System.out.println(obj2);
}
}
最后的结果是obj1被回收,但obj2没被回收
虽然obj2是由obj1赋值引用的,obj1虽然被回收了,但由于obj2是强引用,则垃圾收集器也不会动它
软引用(SoftReference)
是强引用相对弱化了一些的引用,要用java.lang.ref.SoftReference
类来实现
1)当系统内存充足时,软引用的对象不会被回收
2)当系统内存不足时,软引用的对象会被回收
代码Demo
public class SoftReferenceDemo{
public static void softRef_Memory_Enough(){
Object 01=new Object();
//将o1传给软引用softReference
SoftReference<Object> softReference=new SoftReference<o1>;
o1=null;
System.gc();
System.out.println(softReference.get());
}
}
此时内存够用,o2不会被回收
public class SoftReferenceDemo{
public static void softRef_Memory_NotEnough(){
Object 01=new Object();
//将o1传给软引用softReference
SoftReference<Object> softReference=new SoftReference<o1>;
o1=null;
//此处不用自己回收了,因为内存不足时,系统会自动开启垃圾收集器
//System.gc();
try{
byte[] bytes=new bytes[30*1024*1024];
}catch(Throwable e){
e.printStackTrace();
}finally{
System.out.println(softReference.get());
}
System.out.println(softReference.get());
}
}
此时创建了一个大内存引用byte[] bytes=new bytes[30*1024*1024]
,并且把内存缩小
-Xms5m -Xmx5m -XX:-PrintGCDetails
此时的o2被回收
弱引用(WeakReference)
需要用到java.lang.ref.WeakReference
类来实现,比软引用的生存期更短
只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存
代码Demo
public class WeakReferenceDemo{
public static void main(String[] args){
Object o1=new Object();
WeakReference<Object> eeakReference=new WeakReference<>(o1);
o1=null;
System.gc();
System.out.println(weakReference.get());
}
}
此时o2被回收,打印null
软引用和弱引用的应用场景:
有一个应用需要读取大量的本地图片:
i)如果每次读取图片都从磁盘读取,则会严重影响性能
ii)如果一次性全部加载到内存中,又会造成内存溢出
此时使用软引用可以解决这个问题,设计思路:
用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系。在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题
Map<String,SoftReference<Bitmap>> imageCache=new HashMap<String,SoftReference<Bitmap>>();
WeakHashMap案例:
HashMap代码Demo
public sttaic void myHashMap(){
HashMap<Integer,String> map=new HashMap<>();
Integer key=new Integer(1);
String value="HashMap";
map.put(key,value);
key=null;
System.gc();
}
这里key置空时,不会改变map.put()的对象。因为改变的是Integer key=new Integer(1)
这个对象,而map的存储形式是Node对象,不是同一个对象
当垃圾回收后,map里的存储不会被回收
WeakHashMap代码Demo
private static void myWeakHashMap(){
WeakHashMap<Integer,String> map=new WeakHashMap<>();
Integer key=new Integer(2);
String value="WeakHashMap";
map.put(key,value);
key=null;
System.gc();
}
与HashMap类似,key置空时,仍然不影响map的存储。
但这里是用WeakHashMap存储的,则在key置为空时,将key存储的对象回收
虚引用(PhantomReference)
需要java.lang.ref.PhantomReference
类来实现
i)虚引用不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它和没有任何引用一样,在任何时候都可能被垃圾回收期回收
ii)它不会单独使用,也不能通过它访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用
iii)虚引用主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾回收器将对象从内存中清除出去之前做必要的清理工作
iiii)PhantomReference的get方法总是返回null,因此无法访问对应的对象
引用队列(ReferenceQueue)
对象被回收前需要被引用队列保存
代码Demo
public class ReferenceQueueDemo{
public static void main(String[] args){
Object o1=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue();
WeakReference<Object> weakReference=new WeakReference<>(o1,referenceQueue);
o1=null;
System.gc();
System.out.println(referenceQueue.poll());
}
}
结果是引用队列保存了被垃圾回收机制回收的weakReference对象