《深入理解Java虚拟机 (jvm高级特性与最佳实践)》学习笔记——第三章 垃圾收集器与内存分配策略

一、判断对象是否已死的方法

1、引用计数算法

      给对象添加一个引用计数器,每当有一个地方引用它,计数器值+1;引用时效时,计数器值-1;任何时刻计数器为0的对象就是不可能再被使用的。【jvm中并没有使用引用计数算法来管理内存。因为很难解决对象间相互循环引用的问题。】

2、可达性分析算法

      通过一系列的称为“GC Roots”的对象作为起点,从这些节点出发开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,证明此对象不可用。

可作为GC Roots的对象包括下面几种:

a、虚拟机栈(栈帧中的本地变量表)中引用的对象

b、方法区中类静态属性引用的对象

c、 方法区中常量引用对象

d、本地方法栈中JNI(即一般说的Native方法)引用的对象

二、垃圾收集算法

1、标记-清除算法

     两个阶段:标记 、 清除

     首先标记出所有需要回收的对象,在标记完后统一回收所有被标记的对象

     缺点:1、效率低   2、标记清除之后会产生大量不连续的内存碎片

【图片来源百度图片】

2、复制算法【解决效率低问题】

      将内存按照容量划分成大小相等的两块,每次只使用其中一块。当使用的那块用完之后,就将存活的对象复制到另外一块内存上,然后将已经使用过的那块一次清理掉【代价:内存缩小为原来的一半】

【图片来自百度图片】

      商业虚拟机都采用这种收集算法来回收新生代.

      新生代中的对象98%是“朝生夕死”,所以并不需要按照1:1的比例来划分内存空间,而是将空间划分成 1 块较大的Eden区,2 块较小的Survivor区【比例:8:1:1】只有10%的内存空间会被浪费。没办法保证每次回收都只有10%不到的对象存活,当Survivor空间不够用时,需要依赖老年代进行分配担保。【空的Survivor没办法放下之前的Survivor内的对象和Eden空间的对象时,非空Survivor【里面存放的时上一次新生代收集下来存活的对象】空间的对象直接通过分配担保机制进入老年代】

3、标记-整理算法

      两个阶段:标记 、 整理

     首先标记出所有需要回收的对象,在标记完后统一将存活的对象向一端移动,然后直接清理掉边界以外的内存。

【图片来自百度图片】 

4、分代收集算法

新生代:复制算法

老年代:标记-清除算法、标记-整理算法

堆的划分

三、垃圾收集器(java堆:新生代+老年代)

1、新生代垃圾收集器:Serial  、ParNew  、Parallel Scavenge

2、老年代垃圾收集器:CMS 、Serial Old(MSC) 、Parallel Old

3、整个java堆垃圾收集器:G1

Serial收集器:最基本、发展历史最悠久的收集器【jdk1.3.1之前新生代唯一选择】

       单线程:只会使用一个CPU或一条收集线程去完成垃圾收集工作。

       Stop The World: 进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。

       收集算法:复制算法。

       优点:简单高效。对于运行在Client模式下的虚拟机是一个很好的选择。【桌面应用分配给虚拟机的内存一般不是很大,                          停顿时间可以控制在几十毫秒最多100毫秒内】

ParNew收集器:ParNew是Serial的多线程版本,除了使用多线程进行垃圾收集之外,其余与Serial一样。【 所有控制参数、

                            收集算法、Stop The World、对象分配规则、回收策略】

       控制参数:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等

       收集算法:复制算法。

       Stop The World: 进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。     

Server模式下的虚拟机首选的新生代收集器。其中一个与性能无关但很重要的原因:除了Serial收集器,目前只有它能与CMS收集器配合工作】

Parallel Scavenge收集器【“吞吐量优先”收集器】: 多线程

收集算法:复制算法。

关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控制的吞吐量。

吞吐量:CPU用于用于运行用户代码的时间与CPU总消耗时间的比值【吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间) 】

精确控制吞吐量的参数:

控制最大垃圾收集停顿时间  -XX:MaxGCPauseMillis参数 ;GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的

直接设置吞吐量大小XX:GCTimeRatio参数;默认值:99 取值区间:(0,100);垃圾收集时间的比率【吞吐量的倒数】

eg:设置成19  ====>   1/(1+19)  =====> 5%【吞吐量】

Serial Old 收集器:Serial 的老年代版本、单线程、

主要用途:Cilent模式、Server模式下:jdk1.5前与Parallel Scavenge收集器搭配使用;作为CMS的后备方案【并发收集发生Concurrent Mode Failure时使用】

收集算法:标记-整理算法

Parallel Old收集器【jdk1.6才有】:Parallel Scavenge收集器的老年代版本; 多线程

收集算法:标记-整理算法

CMS 收集器【并发低停顿收集器】:以获得最短回收停顿时间为目标的收集器

 收集算法:标记-清除算法

运行过程:

A、初始标记(需要Stop The World):标记一下GC Roots能直接关联到的对象,速度很快

B、并发标记:GC Roots Tracing 的过程

C、重新标记(需要Stop The World):修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录                                                                【时间比初始标记长一些,但是远比并发标记时间短】

D、并发清除

缺点:A、CMS收集器对CPU资源非常敏感

           B、CMS收集器无法处理浮动垃圾

           C、会产生大量的碎片空间【为了解决这个问题,CMS收集器提供了-XX:+UseCMSCompactAtFullCollection默认开启,                     用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理,无法并发,停顿时间变长。                               另一个参数-XX:CMSFullGCsBeforeCompaction默认值0,这个参数表示执行多少次不压缩的FullGC之后,就进行一个压缩的】

G1收集器:

特点:

A、并行与并发

B、分代收集

C、空间整合:

D、可预测的停顿

G1收集器把java堆划分成多个大小相等的独立区域(Region),虽然还保留着新生代、老年代的概念,但是新生代、老年代不再是物理隔离,都是一部分Region(不需要连续)的集合。

G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region

【--】G1中的Region之间的对象引用 、 其他回收器中新生代与老年代之间的对象引用;虚拟机都是使用Remembered Set 来避免全队扫描的。

G1中每个Region都有一个与之对应的Remembered Set ,虚拟机发现程序在对Reference 类型的数据进行写操作时,会产生一个Write Barrier 暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(其他收集器,分代的例子中,就是检查是否老年代中的对象引用了新生代中对象),如果是,就通过CardTable 把相关引用信息记录到被引用对象所属的Region区中的Remembered Set 中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

运行过程:

A、初始标记:标记一下GC Roots能直接关联到的对象,并修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行。

B、并发标记:从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,可与用户线程并发。

C、最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分记录,虚拟机将这段时间对象变化记录在在线程Remembered Set Logs中,最终标记需要把Remembered Set Logs的数据合并到Remembered Set中。这个阶段需要停顿线程,但是可并行执行。

D、筛选回收:先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。也可并行执行,但是没有并行执行,因为只回收部分Region,时间用户可控制的,暂停用户线程将大幅提高收集效率。

四、内存分配与回收策略

Serial/Serial Old【ParNew/Serial Old】 收集器下内存分配和回收策略:

A、对象优先在Eden区分配

B、大对象直接进入老年代【大对象是指需要大量连续内存空间的java对象,如很长的字符串以及数组】

C、长期存活的对象将进入老年代【对象在Survivor区中每熬过一次Minor GC ,年龄增加1岁,增长到一定程度进入老年代【默认15】,可通过 -XX:MaxTenuringThreshold设置】

D、动态对象年龄判定:如果在Survivor空间中年龄相同的所有对象大小总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入老年代。

E、空间分配担保:只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC【jdk1.6 update24】。

 


Minor GC:发生在新生代的垃圾收集动作,因为Java对象大多数具备朝生夕死的特性,所以Minor GC非常频繁,一般回收速度也比较快。

Full GC:发生在老年代的GC,出现了Full GC, 一般会伴随至少一次的Minor GC(但非绝对)。Full GC的速度一般会比Minor GC 慢10倍以上。

 

一下内容来自:https://www.cnblogs.com/heqiyoujing/p/11361307.html

jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.9 默认垃圾收集器G1

 

-XX:+PrintCommandLineFlagsjvm参数可查看默认设置收集器类型

-XX:+PrintGCDetails亦可通过打印的GC日志的新生代、老年代名称判断

 

JDK1.8新特性:

速度更快 – 红黑树
代码更少 – Lambda
强大的Stream API – Stream
便于并行 – Parallel
最大化减少空指针异常 – Optional

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值