第二部分-垃圾回收

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

跟搜索算法root节点:

  • 虚拟机栈,本地方法栈中引用的对象
  • 方法区引用的类静态变量(java8后已经属于堆了) // TODO 待确认
  • 方法区中的常量引用的对象(java8后已经属于堆了) // TODO 待确认

java的强软弱虚引用

  • 强引用不多说,jvm无论如何都不会回收有强引用的对象
  • 软引用,内存快溢出前会回收软引用对象。(安卓端一些图片缓存,全部放进内存又太大,从磁盘慢慢读又太慢,那就用软引用缓存)
  • 弱引用,不确定的时间回收,垃圾收集器发现弱引用对象就会回收它。WeakHashMap就是弱引用的map实现,它的key是关联到一个软引用的,一旦key在外面不存在引用了,会被自动清除。可以将ReferenceQuene传入WeakHashmap的构造方法(constructor)中,这样,一旦这个弱引用指向的对象成为垃圾,这个弱引用将加入ReferenceQuene。看个例子:
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回收,此时弱引用就派上了用场)

  • 虚引用,它唯一的作用就是跟ReferenceQueue一起使用,通知对象被回收。
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对象没有在任何地方被引用(不能反射创建实例)

类回收相关虚拟机参数:

  • -verbose:class可以查看类加载信息
  • -XX:+TraceClassLoading,-XX:+TraceClassUnLoading可以查看类的加载和卸载信息。

垃圾收集算法

  • 标记清除
  • 复制
  • 标记整理
  • 分代收集

垃圾收集器

  • Seria收集器(串行收集器,单线程,采用复制算法)年轻代收集器

  • ParNew(和Seria一样,只是多线程版本而已)年轻带收集器

  • SeriaOld 串行收集器,老年代版本,但是使用标记整理算法。

  • CMS并发老年代收集器。(标记清除)
    初始标记,并发标记,重新标记,并发清除。

  • G1收集器:http://blog.csdn.net/renfufei/article/details/41897113 待学习

内存分配回收策略

  • 优先分配到eden区域

参数:-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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 来自: Android梦想特工队作者: Aaron主页: http://www.wxtlife.com/原...
    技术特工队阅读 4,354评论 0 28
  • 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收的意义 它...
    lyk2112阅读 327评论 0 0
  • 作者:一字马胡 转载标志 【2017-11-12】 更新日志 日期更新内容备注 2017-11-12新建文章初版 ...
    beneke阅读 2,184评论 0 7
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供...
    简欲明心阅读 89,348评论 17 311
  • JVM内存区域 JVM将其管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区...
    luoxn28阅读 688评论 0 0