jvm总结(2)——垃圾收集器与内存分配策略

    有些内存区域因为方法结束或者线程结束时就自动回收了,所以并不需要考虑它们的回收。主要关注的是对java堆和方法区的回收。(方法区怎么回收?)


对象存活分析

    判断对象是否存活的算法有引用计数算法和可达性分析算法。java用的是可达性分析算法。

    引用计数算法原理是,给对象添加一个计数器,引用一次加一,引用失效减一。当计数器为0时对象就不可能被使用,所以可以回收该对象。
这个算法简单高效但是有一个缺点就是不能处理对象引用循环的情况,即对象A引用对象B,B引用A,除此之外没有任何其他对象引用A和B,这样子AB永远不可能被使用,因为计数器值为1也不会被回收。

    可达性分析算法,让一些自动回收(我还不确定选择的标准)的对象作为GCRoot,每个被引用的对象都会有一条到GCRoot的引用链。回收时通过GCRoot来搜索对象的引用,若找不到这个对象就说明这个对象没有被引用是可回收的(GCRoot到这个对象不可达)。

可作为GCRoot的对象有:

  • 虚拟机栈的局部变量表中引用的对象(栈的)(执行上下文)
  • 本地方法引用的对象
  • 方法区的静态属性引用的对象(方法区的)(全局性的引用)
  • 方法区中常量引用的对象

引用

引用分为强引用、软引用、弱引用、虚引用。

  • 强引用,new出来的一类引用,只要引用还在就不会被回收。
  • 软引用,内存不足时会强制回收这类引用,SoftReference类实现。
  • 弱引用,活不到下一次gc,WeakReference类实现。
  • 虚引用,虚的,不能取得对象实例,唯一作用是在对象被gc时能收到一个系统通知,PhantomReference类实现(在对象被回收前将虚引用加入ReferenceQueue中,通过这个队列可以知道那些对象被回收了)。

关于finalize()方法

    Object的protected方法,可在gc回收前调用这个方法,实现资源清理或者自救。
    若gc第一次标记时,判断到对象需要执行finalize方法,就将它加入一个队列中,另开线程执行这个方法,但不会等它执行完。
    如果对象在finalize方法中完成了自我拯救,在第二次标记时它会被移除出“即将回收”集合。

  • finalize方法被覆盖了才会被执行
  • 只能执行一次
  • 运行代价高昂且没必要,最好忘了它。

垃圾收集算法

    主要有 标记-清除算法、标记-整理算法、复制算法、分代收集算法。

标记-清除
    先标记出所有要清除的对象,然后再统一回收这些对象。
不足:
    效率低。
    空间碎片多,给大对象分配内存时找不到足够的连续内存,会提前触发另一次gc。

复制
    将内存分成两半,每次只使用其中一半,在用完时将存活对象复制到另一半上去,再将那用完的半部分空间都清理掉。
不足:可用内存减少了

    虚拟机将新生代分为了Eden区和两个Survivor区,大小 8:1:1。每次只用Eden和一块Survivor,回收时将存活对象复制到另一块Survivor中。
    可以这样分是因为新生代中大多是朝生夕死的对象。但存活对象也有可能会多余10%,所以需要分配担保机制,在Survivor空间不够时,让其他空间比如老年代提供分配担保,把存活对象复制到这些空间里。(是部分还是全部复制过去?咦担保是用的复制吗)

标记-整理
    标记所有可回收对象,然后将存活对象全部向一端移动,最后清除掉边界外的内存。

    老年代的选择。

分代收集算法
    根据对象存活时间来划分内存。
    一般将java堆分为新生代和老年代,然后可以根据不同年代的特点采用不同的收集算法。新生代朝生夕死存活对象少,所以可用复制算法。老年代顽强存活对象多,又没有其他空间给它担保所以只能选标记清除和标记整理。


枚举根结点、安全点和安全区域

    枚举根结点时为了防止引用关系变化,需要阻塞所有运行java code的线程(stop the world),VM操作相关的线程不会被阻塞,运行native code的线程如果不与javacode交互也不会被阻塞。
    另外不需要检查所有引用位置来找全GCRoot,数据结构OopMap记录了引用的位置。
    但是因为很多指令会导致引用关系变化,即OopMap内容改变,又不可能给每条指令后面都生成对应的OopMap。所以OopMap只在特定的位置生成,这些位置就是安全点。
    安全点是指一些特殊的位置,在这些位置线程状态可以被确定,使jvm安全的进行一些操作(比如gc,程序运行时并非在所有地方都能停顿下来进行gc,到达安全点才能停下来)。
    安全点的选定不能太少,让gc等待时间太长(自己的理解:假设只有一个安全点,每次gc都要等待线程运行到这个安全点才能开始,安全点多点的话,线程短时间内有很大几率碰到一个安全点,gc就不用等那么久才能开始),也不能过于频繁增加负荷。

所以,安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的——因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这个原因而过长时间运行,“长时间执行”的最明显特征就是指令序列复用,例如方法调用、循环跳转、异常跳转等。

(这一段看不懂)
这些特定的位置主要在:
1、循环的末尾
2、方法临返回前 / 调用方法的call指令后
3、可能抛异常的位置

    线程自己响应中断:在安全点设置一个标志,轮询这个标志,若为真就自己中断挂起。
    若线程阻塞,无法轮询,就需要安全区域了,安全区域可以看作是安全点的扩展。线程执行到安全区域时标示自己已经进了safe region,gc时不用管这些线程,当线程要出这个区域时查看gc是否已经完成,没完成需要等到gc完成才能出去。


垃圾收集器

Serial收集器
     单线程,需要stoptheworld,简单高效,适用于client模式的VM,新生代的选择,SerialOld可用于老年代。
     新生代复制算法,SerialOld老年代标记-整理算法

ParNew收集器
     多线程,Serial的多线程版,需要暂停用户线程,server模式VM中首选的新生代收集器。
除了Serial唯一能与CMS收集器配合工作的收集器。

Parallel Scavenge收集器
     多线程,新生代收集器,复制算法。吞吐量优先收集器。吞吐量是代码运行时间和cpu总运行时间的比值(即代码时间+gc时间)。
     要减少停顿时间就要牺牲新生代空间,新生代空间小了,停顿时间也就变小了,但是相应的gc次数变多了,吞吐量也降了下来。
-XX:GCTimeRatio gc时间占总时间的比值
-XX:MaxGCPauseMillis 最大停顿时间
-XX:+UseAdaptiveSizePolicy 自适应策略

Serial Old收集器
jdk1.5 前与parallel搭配使用
作为CMS的后备预案

Parallel Old 收集器
1.5后 与Parallel搭配使用。

image.png

CMS收集器
以最短停顿回收时间为目标,标记清除算法,并发低停顿收集器
只有初始标记和重新标记需要阻塞用户线程

  • 初始标记:标记GCRoots直接关联的对象(stop the word)
  • 并发标记:GCRoots Tracing过程,用户线程可并发执行
  • 重新标记:修改并发标记阶段,对象引用改变了的那一部分标记对象的标记(stop the world),时间比并发标记短的多
    (有新的引用即新的对象出现怎么办怎么确定它与垃圾对象的区别??再tracing一次吗??
    标记是怎么标记的??三色标记法了解一下)
  • 并发清除

缺点:

  • 对CPU资源敏感。
    并发时会占用一部分线程,默认启动的垃圾收集线程数是(cpu数量+3)/4。
  • 无法处理浮动垃圾。(并发时,标记的对象用户线程不要了)
    并发时用户线程会产生新的垃圾,这一部分垃圾只能在下次gc时处理。所以gc时需要预留一部分内存来给用户使用,如果预留的内存不够会出现“Concurrent Mode Fail”失败导致另一次full gc,此时会临时启用SerialOld收集器重新对老年代进行收集。
    默认老年代使用了92%空间后激活CMS收集器。
  • 大量空间碎片产生。
    -XX:+UseCMSCompactAtFullCollection 默认开启,在没足够内存需要full gc时进行碎片整理
    -XX:CMSFullGCsBeforeCompaction 默认0,在进行多少次不压缩的fullgc后,跟着来一次压缩(碎片整理)的fullgc

G1收集器
它java堆的内存分布与其他收集器不一样,它将java堆划分成了很多个相同大小的区域(region)。

  • 空间整合:不会产生空间碎片,整体是标记-整理算法(以区域为基础进行操作),局部是复制算法(将区域内的存活对象复制到另一个区域)。
  • 分代收集:各个年代是一部分region的集合。


    image.png
  • 并行
  • 可预测的停顿:能指定垃圾收集时间,因为G1会在后台根据每个区域垃圾的价值(回收所需时间和回收获得的空间的经验值)维护一个优先列表,每次根据允许的垃圾收集时间优先回收最大的区域。

老年代引用新生代的对象的情况:

老年代有一个CardTable,若老年代中有引用新生代的对象,就将cardtable对应的索引记录到新生代对象所属的RememberSet中,这样子MinorGC就不用搜索全堆,只需将RSet加入根结点枚举范围扫描特定的地方。


image.png

运行步骤:

  • 初始标记,停顿线程,但时间很短
  • 并发标记,耗时长
  • 最终标记,应该还是有浮动垃圾?
  • 筛选回收,将各个region根据价值进行排序,根据用户要求的回收时间制定回收计划。停顿线程,并行执行(真并行?)

(JIT编译后被拆散为标量类型栈上分配??)

对象的内存分配规则
  • 优先在Eden区分配,没有足够内存会发起MinorGC
  • 大对象直接进入老年代(长字符串或数组)
    -XX:PretenureSizeThreshold 大对象大小,大于这个值的对象直接分配在老年代
  • 长期存活的对象进入老年代,熬过一次MinorGC,年龄加1,默认15岁进老年代
    -XX:MaxTenuringThreshold 设置进入老年代的年龄阀值
  • 动态对象年龄判定, 如果survivor中相同年龄的所有对象大小总和超过survivor的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
  • 空间分配担保 ,gc时,若survivor空间不足,将对象放进老年代。
    (jdk6)MinorGC前,需要判断是否安全,先检查新生代对象总大小是否小于老年代连续空间的大小或者历次晋升到老年代的对象大小的平均值,是的话可进行minorGC(若minorGC时老年代空间不足,担保失败,只好重新发一次fullGC),不是的话改为fullGC。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容