第二部分-垃圾回收

2017-08-20  本文已影响0人  炫迈哥

应用计数器法没办法规避循环引用,忽略它。。。

跟搜索算法root节点:

java的强软弱虚引用

import java.util.HashMap;  
import java.util.Iterator;  
import java.util.Map;  
import java.util.WeakHashMap;  

public class Test {  
    public static void main(String[] args) throws Exception {  
        String a = new String("a");  
        String b = new String("b");  
        Map weakmap = new WeakHashMap();  
        Map map = new HashMap();  
        map.put(a, "aaa");  
        map.put(b, "bbb");  

          
        weakmap.put(a, "aaa");  
        weakmap.put(b, "bbb");  

        map.remove(a);  

        a=null;  
        b=null;  

        System.gc();  
        Iterator i = map.entrySet().iterator();  
        while (i.hasNext()) {  
            Map.Entry en = (Map.Entry)i.next();  
            System.out.println("map:"+en.getKey()+":"+en.getValue());  
        }  

        Iterator j = weakmap.entrySet().iterator();  
        while (j.hasNext()) {  
            Map.Entry en = (Map.Entry)j.next();  
            System.out.println("weakmap:"+en.getKey()+":"+en.getValue());  

        }  
    }  

      
}  

当把a和b都置为null后,hashmap中也不存在对它的引用了(remove掉了),外边a也被置null了,weakhashmap将会自动移除a为key的记录,b虽然被置为null了,但是new String("bbb")出来的对象在hashmap中还存在引用,所以不会被回收。
(弱引用最大的用处是,你需要用一个对象,但也只是用一用,不能影响它的垃圾回收,比如需要监控虚拟机中某些对象的属性值,如果直接用强引用获取,那么被监控的对象将不会被gc回收,此时弱引用就派上了用场)

import java.lang.ref.PhantomReference;  
import java.lang.ref.Reference;  
import java.lang.ref.ReferenceQueue;  
import java.lang.reflect.Field;  
  
public class Test {  
    public static boolean isRun = true;  
  
    @SuppressWarnings("static-access")  
    public static void main(String[] args) throws Exception {  
        String abc = new String("abc");  
        System.out.println(abc.getClass() + "@" + abc.hashCode());  
        final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();  
        new Thread() {  
            public void run() {  
                while (isRun) {  
                    Object obj = referenceQueue.poll();  
                    if (obj != null) {  
                        try {  
                            Field rereferent = Reference.class  
                                    .getDeclaredField("referent");  
                            rereferent.setAccessible(true);  
                            Object result = rereferent.get(obj);  
                            System.out.println("gc will collect:"  
                                    + result.getClass() + "@"  
                                    + result.hashCode() + "\t"  
                                    + (String) result);  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            }  
        }.start();  
        PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,  
                referenceQueue);  
        abc = null;  
        Thread.currentThread().sleep(3000);  
        System.gc();  
        Thread.currentThread().sleep(3000);  
        isRun = false;  
    }  
}  


我们可以声明虚引用来引用我们感兴趣的对象,在gc要回收的时候,gc收集器会把这个对象添加到referenceQueue(如上例中,abcWeakRef虚引用就对abs这个对象感兴趣,abc被回收时会被放入referenceQueue中,我们只用监听referenceQueue就知道哪些对象被回收了),这样我们如果检测到referenceQueue中有我们感兴趣的对象的时候,说明gc将要回收这个对象了。此时我们可以在gc回收之前做一些其他事情(如安卓图片滚动显示,每次只显示一张图片,但是图片特别大,内存只能容得下一张图片的大小,此时就需要虚引用来监听前一张图片是否已经被移除,移除之后才能加载下一张图片进来)

finalize方法

没有实现finallize方法的对象不会执行这一堆逻辑,对象回收时干的事情,执行后对象会被放入F-queue中,如果此时对象又被引用上,对象将起死回生。每个对象虚拟机只会执行一次finalize方法。建议对象回收监听使用虚引用(幽灵引用)来实现。

回收方法区

某个字符串已经没有任何引用指向它时即可回收。

  1. 没有任何类实例(全部被回收)
  2. 加载该类的类加载器已经被回收
  3. 类的Class对象没有在任何地方被引用(不能反射创建实例)

类回收相关虚拟机参数:

垃圾收集算法

垃圾收集器

内存分配回收策略

参数:-Xms20M -Xmx20M 堆最小值及最大值,-Xmn10M堆的新生代大小,设置为10MB后,老年代大小就为20m-10m=10m了。 -XX:SurvivorRatio=8代表survicor:eden=1:8,设置为8后,新生代中eden区域就占8/10,两个survivor分别占1/10;

因为对象太大先存入eden区,再进survivor进行复制算法拷贝代价就会比较大。
可以使用虚拟机参数-XX:PretenureSzieThreshold:13723来设定判断大对象的阈值(需要注意,这里单位默认为b,写的时候不能自定义单位,另外,只有parNew和Seria收集器会识别该参数)

并发那一块的笔记里对对象头的介绍,除了偏向锁等信息等之外就有一个age属性,记录对象的垃圾收集年龄,在survivor区每次被复制都会+1,虚拟机默认达到15岁进入老年代,可以手动设置年龄阈值,-XX:MaxTenuringThreshold:5

tips:虚拟机有另外一个动态年龄判断规则,如果某个年龄的对象大小占据了survivor区的一半,则会将年龄大于等于该年龄的所有对象晋升到老年区。

担保,就是年轻代没有容纳能力了,老年代替你存。(这里老年代替年轻代存的是新的需要分配空间的对象还是survivor区的最老的对象?????!!!回答:当然是最老的对象了咯,除非是大对象)
!!空间分配担保是一直都开启的,HandlePromotionFailure参数只是设置是否允许担保失败。

HandlePromotionFailure参数配置的影响:每次minorgc前先判断老年代是否能容纳全部当前年轻代的大小,成立->直接minorgc即可,不成立-> (老年代剩余空间小于平均晋升大小 || 不允许担保失败 -> fullgc , else -> minorgc)

结论:

  1. HandlePromotionFailure设置成了false,每次minorgc时只要老年代容不下所有年轻代了,都会被改为fullgc!!
  2. 设置为true后,只有靠平均晋升大小来判断是否触发fullgc了,这样的经验判断,肯定会有失误的时候,一旦某次晋升对象大小大于经验值,此时会再触发一次fullgc
  3. 所以,一般都会将HandlePromotionFailure设置为true,不要让虚拟机频繁的fullgc
上一篇 下一篇

猜你喜欢

热点阅读