垃圾回收
如何判断对象可以回收
引用计数法
只要一个对象被其他变量所引用,就让其计数加一,引用两次就加二,如果某一个变量不再引用,就让其计数减一,如果其计数为0,就可以进行回收
弊端:循环引用
可达性分析算法(jvm使用的一种算法)
首先确定一系列根对象(肯定不能当成垃圾回收的对象),在垃圾回收前,首先对堆中所有对象进行扫描,看此对象是否被根对象直接或间接引用,如果被引用,则此对象不能被垃圾回收,反之会被垃圾回收
哪些对象可以作为根对象?
使用Eclipse中的Memory Analyzer(MAT),是一个java堆的分析工具,可以帮助你找到内存的泄露,减少内存的耗费
四种引用
如图:
实线箭头表示强引用,虚线的表示其他引用
强引用
平时用的所有引用都属于强引用,如:List<Object> list1 = new ArrayList<>();
,只有所有GC Roots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
软引用
- 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
- 可以配合引用队列来释放软引用自身
弱引用
- 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
软引用和弱引用比较像,只要对象没有被直接的强引用所引用,那么当垃圾回收时,都有可能被回收,当没有强引用对象引用时:A2对象,如果没有B对象直接引用,当内存不足时,会被垃圾回收,A3对象,如果没有B对象直接引用,发生垃圾回收时,不管内存够不够,都会回收A3对象,这两种既可以配合引用队列使用,也可以不配合引用队列使用
虚引用
- 必须配合引用队列使用,主要配合 ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference
Handler线程调用虚引用相关方法释放直接内存
终结器引用
- 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法,第二次GC时才能回收被引用对象
用以下代码演示软引用:
-Xmx20m -XX:+PrintGCDetails -verbose:gc,设置参数
public class Demo_12 {
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 < 6; 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 Demo_13 {
private static final int _4MB=4*1024*1024;
public static void main(String[] args) {
//list-->SoftReference(软引用对象)-->byte[]
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("循环结束:"+list.size());
for(SoftReference<byte[]>ref:list){
System.out.println(ref.get());
}
}
}
结果如下:
可以看到四个null也被回收了
用以下代码演示弱引用:
/*-Xmx20m -XX:+PrintGCDetails -verbose:gc*/
public class Demo_14 {
private static final int _4MB=4*1024*1024;
public static void main(String[] args) {
List<WeakReference<byte[]>> list=new ArrayList<>();
for (int i = 0; i < 5; i++) {
WeakReference<byte[]>ref=new WeakReference<>(new byte[_4MB]);
list.add(ref);
for(WeakReference<byte[]>w:list){
System.out.println(w.get()+" ");
}
System.out.println();
}
System.out.println("循环结束:"+list.size());
}
}
运行结果如图:
将循环次数改成10,结果如下:
可以发现,触发了一次full gc,使得前面的都变成了null,因为弱引用本身也占一点内存,清除弱引用和清除软引用方法相同