JVM的垃圾收集器策略

说明:本篇属于读书笔记,大量参考《深入理解Java虚拟机》

判断对象不可用的几种方式

引用计数量法

  • 通过计算对象被引用的次数来判断该对象是否还有被引用,这种方式的判断效率较高,因为判断逻辑比较简单嘛,但是无法解决对象之间的循环引用问题,加入A对象引用了B对象,而B对象也引用了A对象,而A对象和B对象不再被其他对象所引用,那么A和B对象都是不可达的,但是引用计数法会导致A和B的引用计数都不为0,所以不会被垃圾收集器回收,因此JVM主流的虚拟机都不会用这种方式来判断对象是否可达

可达性分析算法

  • 可达性分析算法就是所谓的Gc Roots,在MAT的内存泄漏分析中就是根据对象是否存在Gc Roots来判断对象是否还存在引用,从而确定对象是否泄漏;当一个对象到Gc Roots没有任何引用链的时候就认为该对象不可达,不可达的对象就是可回收对象
  • 在Java中,可作为Gc Roots的对象包括以下几种:
  1. 虚拟机栈中的引用对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法区中的JNI引用的对象
    为什么以上四种可以作为Gc Roots?因为以上四种在Java程序运行起来之后就基本不会被回收

Java中的对象引用

  • 强引用,new出来的对象就是属于强引用,只要对象存在强引用,垃圾回收器就不会回收该对象
  • 软引用,如果对象存在软引用,垃圾回收器在发生Gc操作的时候并不会被回收,而是在程序内存紧张的时候才会被回收,例如抛出OOM之前会被回收,SoftReference类来实现软引用
  • 弱引用,如果对象存在弱引用,那么在每次垃圾回收器发生Gc操作的时候都会被回收,WeakReference来实现弱引用
  • 虚引用,虚引用是最弱的引用关系,虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中,虚引用由PhantomReference类来实现,如下:
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

对象是否可以被回收的判断条件

  • JVM发现对象到Gc Roots已经没有引用链了,这时候对象会被第一次标记,并判断该对象是否有必要执行finalize()方法,如果对象没有实现finalize()方法或者finalize()方法已经被JVM调用过那JVM认为没有必要执行finalize()方法,如果对象没必要执行finalize()方法的话就满足被回收的条件,如果对象还需要被执行finalize()方法,那对象会被放到F-Queue队列中并由低优先级的Finalizer线程区执行finalize()方法,但是并不会等待finalize()方法执行结束,因为如果finalize()方法中的实现存在问题,例如卡死,那么会导致F-Queue队列都处于等待状态,这将导致整个JVM内存回收都会卡住,这绝对是不允许的,所以在对象被回收的时候并不保证finalize()方法一定会被执行到,只能说对象被回收之前通常会执行finalize()方法,如果对象有重载该方法

方法区的回收

  • 方法区通常存储类的信息,例如常量,方法等,因此这些区域一般不会发生垃圾回收,也就是通常所说的永久代,但是其实永久代也是有垃圾回收的,只不过回收率很低,永久代的垃圾回收主要是回收废弃常量和无用类
  • 废弃常量,所谓的废弃常量是指程序中是否还存在引用该常量的String对象,如果没有,那么该常量就是废弃常量,垃圾回收器发生Gc的时候就会回收
  • 无用类,无用类指的是该类的所以实例都已经被回收,加载该类的ClassLoader也已经被回收,该类对应的Class在程序中没有被引用,基于以上三点,通常情况下是不会存在无用类的,因为加载该类的ClassLoader通常是不会被回收,但是一些动态代理,动态生成的Class,如CGLib,javassist等字节码框架,android中的插件化自定义的ClassLoader加载的外部dex,都需要具备类的卸载功能来避免方法区不内存溢出

垃圾回收算法

标记-清除算法(Mark-Sweep)

  • 标记需要被回收的对象,然后再执行对象的回收操作(清除),但是回收效率地下,而且这种回收算法会留下大量不连续的内存碎片,如果内存中有大量的碎片,可能会导致当程序申请一块比较大的连续内存的时候由于内存中的连续内存不够会提前触发一次Gc来获取到足够的连续内存

复制算法

  • 该算法是将内存分为大小相等的两块,每次只使用其中的一块,当这一块用完了,就将还活着的对象复制到另一块内存中,然后把已经使用过的内存空间进行一次清理,这种回收算法不会产生大量的内存碎片,而且每次只是针对一半的内存块进行操作,效率较高,但是不足的是内存的利用率较低,相当于只有一半的内存得到了有效的利用
  • 现在很多虚拟机采用的是:将内存分为一块较大的Eden区域和两块较小的Survivor区域,每次使用Eden区域和其中一个Survivor,在进行垃圾回收的时候把Eden区和在使用的Survivor区中活着的对象都复制到另一块Survivor当中,然后再清理Eden和Survivor区域,当另一块Survivor区也不够存放存活下来的对象的时候,这时候会把这些对象变成老年代

标记-整理算法

  • 标记整理算法和标记清除算法类似,都是先标记需要被回收的对象,然后标记整理算法会再对此时已经被标记好的内存进行整理,把存活的对象都向一端移动,然后清理调端边界以外的内存

分代收集算法

  • 新生代对象的有经常被回收只有少量存活,而老年代对象的存活率较高,所以对新生代对象可以采用复制算法来回收,对于老年代对象可以采用标记-清理活着标记-整理算法来回收

Stop The World

  • Java虚拟机在发生Gc的时候,由于是用Gc Roots的方式来判断对象是否要被回收,所以当发生Gc的时候需要暂停虚拟机的所有线程,等Gc结束后才继续执行,因此Gc的时候会造成程序卡顿,之所以要暂停所有线程的原因是Gc的发生是基于当前的Gc Roots引用链,如果线程还在继续执行,那么引用链很可能发生改变,这样Gc可能把还有引用链的对象给回收了,那就乱套了

对象的内存分配于回收

  • 通常对象在新生代会被分配在Eden区因为大部分对象都会发生频繁的常见并很快就会被回收,当Eden区的内存不够了,这时候会触发一次Monitor Gc,当Gc之后Eden区的内存还是不够用,那么此时Edne区的存活对象就会被移入老年代
  • 大对象直接进入老年代,因为老年代对象存活的几率大,不会经常发生Gc,所以针对大对象的创建和回收需要耗费较大的资源的这种就直接放入老年代来避免大对象频繁的创建与Gc
  • 当对象在Eden区域存在足够久的时候(长期存活的对象),这时候就把该对象放入老年代,因为该对象不经常被回收,长期存活,所以不应该放在经常发生Gc的Eden区,以此来减少Eden区的对象,从而加快Eden的Gc效率
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容