强引用
new 创建的对象
当内存不足时,jvm开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,即使该对象以后永远都不会被用到jvm也不会回收。
特点:沿着GC root的引用链能找到对象,GCroot 都断开引用时才能进行垃圾回收。
软引用(SoftReference)
没被直接的强引用所引用,垃圾回收并且内存不够时会释放掉软引用。就是系统内存充足不会回收,内存不够会回收。
应用举例
/**
* 演示软引用
* -Xmx20m -XX:+PrintGCDetails -verbose:gc
*/
public class SoftDemo {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) throws IOException {
/*List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new byte[_4MB]);
}
System.in.read();*/
soft();
}
public static void soft() {
// list --> SoftReference --> byte[]
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
System.out.println("循环结束:" + list.size());
for (SoftReference<byte[]> ref : list) {
System.out.println(ref.get());
}
}
}
引用队列
/**
* 演示软引用, 配合引用队列
*/
public class Demo2_4 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null) {
list.remove(poll);
poll = queue.poll();
}
System.out.println("===========================");
for (SoftReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}
}
弱引用 (WeakReference)
没有直接的强引用,垃圾回收直接回收弱引用对象。不管内存是否够用,只要触发GC就回收掉。
注:可以配合引用队列使用,软引用、弱引用回收后进入引用队列,进一步处理,释放自身。
软引用和弱引用的使用场景
问题:
假如有一个应用需要读取大量的本地图片:
- 如果每次读取图片都从硬盘读取则会严重影响性能
- 如果一次性全部加载到内存中又可能造成内存溢出
此时使用软引用或者弱引用可以解决这个问题,设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用直接的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效的避免了OOM的问题。
Map<String,SoftReference<Bitmap>> imageCache = new HashMap<String,SoftReference<Bitmap>>();
进而提出一个问题那你知道WeakHashMap吗?
public class WeakHashMapDemo {
public static void main(String[] args) {
myHashMap();
myWeakHashMap();
}
private static void myHashMap() {
HashMap<Integer,String> weakHashMap = new HashMap<>();
Integer key =new Integer(1);
String value = "WeakHashMap";
weakHashMap.put(key,value);
System.out.println(weakHashMap);
key = null;
System.out.println(weakHashMap);
System.gc();
System.out.println(weakHashMap);
}
private static void myWeakHashMap() {
WeakHashMap<Integer,String> weakHashMap = new WeakHashMap<>();
Integer key =new Integer(2);
String value = "WeakHashMap";
weakHashMap.put(key,value);
System.out.println(weakHashMap);
key = null;
System.out.println(weakHashMap);
System.gc();
System.out.println(weakHashMap);
}
}
不管内存是否充足GC之后马上回收掉,这样的类方便做高速缓存等
虚引用 (PhantomReference)
形同虚设,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被回收,必须配合引用队列(ReferenceQueue)使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一致确保对象被finalize以后,做某些事的机制。PhantomReference的get()总是返回null,无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被gc,用来事项比finalization机制更灵活的回收操作。
换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。算是临终遗言吧emmmmm…
Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
垃圾回收时,虚引用放入引用队列,例如ByteBuffer回收后,Cleaner放入引用队列,ReferenceHandler线程定时查找是否有新入队的虚引用Cleaner,释放直接内存。
public class ReferenceQueueDemo {
public static void main(String[] args) throws InterruptedException {
phantom();
}
private static void phantom() throws InterruptedException {
System.out.println("我是分割线");
Object o = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o,referenceQueue);
System.out.println(o);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println("phantom===============");
o = null;
System.gc();
Thread.sleep(500);
System.out.println(o);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
}
}
终结器引用 (FinalReference)
没有强引用引用对象时,虚拟机创建终结器引用,垃圾回收时将终结器引用加入引用队列,注意此时没有进行垃圾回收,一个优先级很低的线程Finalizer查看引用队列是否有终结器引用,找到这个对象调用finallize()方法就可以回收垃圾。
不推荐使用,又慢又复杂。
总结
Java中可以作为GC Roots的对象: