对象是否以死的判断算法

1.引用计数算法:给每个对象添加一个引用计数器,每当有一个地方引用他的时候,计数器就加一,当引用失效的时候计数器就减一,当计数器为0 的时候表示对象是不可能再被利用了。但是有个缺点是,对象之间相互引用而导致了计算器永远不为0,因此无法进行垃圾收集。虚拟机中不是运用这个算法进行回收的。

2.可达性分析算法:通过一系列的称为“GC roots” 的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GCroot中没有任何的引用链的时候,证明对象是不可用的。

可作为GCroots的对象的

1,虚拟机栈中引用的对象

2.方法区中类静态属性引用的对象

3方法区中常量引用的对象

4,本地方法栈中的JNI引用的对象

再谈引用:

强引用:Object obj=new Object() 这类的引用,只要强引用还在,垃圾收集器永远不会回收被引用的对象

软引用:用来描述一些还有用但是并非必需的对象,对于软引用关联着的对象,在系统发生内存溢出之前,将把这些对象列入回收范围中进行二次回收,如果这次还没有收到足够的内存,才回抛出内存溢出异常。

弱引用:用来描述非必需的对象,强度比软引用要弱一些,被弱引用关联的对象只能生存到下次垃圾回收之前。当垃圾收集开始的时候,无论当前的内存是否足够都会收掉被弱引用关联的对象。

虚引用:幽灵引用,幻影引用,最弱的引用,一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用取得一个对象实例。为一个对象设置虚引用的关联的目的是能够在对象被垃圾收集器回收的时候收到一个系统的通知。

回收方法区:

无用的类:第一:该类的所有的实例都已经被回收,java堆中不存在该类的任何的实例,第二,加载该类的的ClassLoader已经被回收,第三,该类对应的java.lang.class对象没有在其他的地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集器:

标记-清除算法

最基础的算法:首先标记出所有需要回收的对象,在标记完成之后统一回收被标记的对象。缺点是:标记和清除的效率都不高,第二是清除后的产生大量的不连续的内存碎片,碎片太多可能导致在将来的程序运行的过程中需要分配较大对象的时候,无法找到足够连续的内存,而不得不触发另外一次的垃圾收集操作。

复制算法:

把内存分为两块大小相等的块,每次使用一块,当这一块使用完之后,把还存活的对象复制到另外一块,然后对使用过的内存空间清理掉,使得每次回收只是对一半区进行内存回收,,内存分配的时候就不用考虑内存碎片等复杂问题了。但是复杂算法只使用一半的内存,代价太高。

标记整理算法:

标记之后对存活的对象移到一端,然后直接清理掉端边界以外的内存.

分代收集算法:

java的堆分为新生代和年老代,新生代选择使用复制算法,老年代使用标记清除或者标记整理算法。

垃圾收集器:


Serial收集器:

单线程,收集的时候会停止其他线程的运行。新生代使用复制算法。是Jvm client模式下默认的新生代收集器,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率,因此是运行在Client模式下的虚拟机的不错选择(比如桌面应用场景)。


parNew收集器:

多线程,暂停用户的线程,新生代使用复制算法,年老代使用标记整理算法


是运行在 Service模式下虚拟机中首选的 新生代收集器,其中一个与性能无关的原因就是 除了Serial收集器外,目前只有ParNew收集器能与CMS收集器配合工作。
  PreNew收集器在单CPU环境中绝对没有Serial的效果好,由于存在线程交互的开销,该收集器在超线程技术实现的双CPU中都不能一定超过Serial收集器。默认开启的垃圾收集器线程数就是CPU数量,可通过-XX:parallelGCThreads参数来限制收集器线程数

Parallel Scavenge收集器:

新生代的垃圾收集器,目标是达到一个可控的吞吐量。它也是使用复制算法的收集器,又是并行多线程收集器。parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而parallel Scavenge收集器的目标则是达到一个可控制的吞吐量吞吐量= 程序运行时间/(程序运行时间 + 垃圾收集时间),虚拟机总共运行了100分钟。其中垃圾收集花掉1分钟,那吞吐量就是99%。


短停顿时间适合和用户交互的程序,体验好。高吞吐量适合高效利用CPU,主要用于后台运算不需要太多交互。
  提供了两个参数来精确控制吞吐量:1.最大垃圾收集器停顿时间(-XX:MaxGCPauseMillis    大于0的毫秒数,停顿时间小了就要牺牲相应的吞吐量和新生代空间),2.设置吞吐量大小(-XX:GCTimeRatio    大于0小于100的整数,默认99,也就是允许最大1%的垃圾回收时间)。
  还有一个参数表示自适应调节策略(GC Ergonomics)(-XX:UseAdaptiveSizePolicy)。就不用手动设置新生代大小(-Xmn)、Eden和Survivor区的比例(-XX:SurvivorRatio)今生老年代对象大小(-XX:PretenureSizeThreshold),会根据当前系统的运行情况手机监控信息,动态调整停顿时间和吞吐量大小。也是其与PreNew收集器的一个重要区别,也是其无法与CMS收集器搭配使用的原因(CMS收集器尽可能地缩短垃圾收集时用户线程的停顿时间,以提升交互体验)。

Serial Old(串行GC)收集器

Serial Old是Serial收集器的老年代版本,它同样使用一个单线程执行收集,使用“标记-整理”算法。主要使用在Client模式下的虚拟机。
        
               Serial/Serial Old收集器运行示意图(表示Serial和Serial Old搭配使用) 
  如果在Service模式下使用:1.一种是在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用,因为那时还没有Parallel  Old老年代收集器搭配;2.另一种就是作为CMS收集器的后备预案,在并发收集发生Concurrent Model Failure时使用。

Parallel Old(并行GC)收集器

CMS(并发GC)收集器

  CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是HotSpot虚拟机中的一款真正意义上的并发收集器,第一次实现了让垃圾回收线程和用户线程(基本上)同时工作。用CMS收集老年代的时候,新生代只能选择Serial或者ParNew收集器。
  CMS收集器是基于“标记-清除”算法实现的,整个收集过程大致分为4个步骤:
①.初始标记(CMS initial mark)
②.并发标记(CMS concurrenr mark)
③.重新标记(CMS remark)
④.并发清除(CMS concurrent sweep)
     其中 初始标记、重新标记这两个步骤任然需要停顿其他用户线程(Stop The World)初始标记仅仅只是标记出GC ROOTS能 直接关联到的对象,速度很快, 并发标记阶段是进行GC ROOTS 根搜索算法阶段,会判定对象是否存活。而重新标记阶段则是为了 修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。
     由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以整体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
      
                CMS收集器运行示意图
CMS收集器的优点: 并发收集、低停顿,但是CMS还远远达不到完美,器主要有三个显著缺点:
  1.CMS收集器 对CPU资源非常敏感。在并发(并发标记、并发清除)阶段,虽然不会导致用户线程停顿,但是会占用CPU资源而导致应用程序变慢,总吞吐量下降。 CMS默认启动的回收线程数是:(CPU数量+3) / 4。 收集器线程所占用的CPU数量为:(CPU+3)/4=0.25+3/(4*CPU因此这时垃圾收集器始终不会占用少于25%的CPU,因此当进行并发阶段时,虽然用户线程可以跑,但是很缓慢,特别是双核CPU的时候,已经占用了5/8的CPU,吞吐量会很低。为了解决这种情况,产生了“ 增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)。就是采用抢占方式来模拟多任务机制,就是在并发(并发标记、并发清除)阶段,让GC线程、用户线程交替执行,尽量减少GC线程独占CPU,这样垃圾收集过程更长,但是对用户程序影响小一些。实际上i-CMS效果很一般,目前已经被声明为“deprecated”。
  2.CMS收集器 无法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而导致另一次Full  GC的产生。由于CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“ 浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集, 需要预留一部分内存空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了 68%的空间时就会被激活,也可以通过参数-XX:CMSInitiatingOccupancyFraction的值来 提高触发百分比,以降低内存回收次数提高性能。JDK1.6中,CMS收集器的启动阈值已经提升到92%。要是CMS运行期间预留的内存无法满足程序其他线程需要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置的过高将会很容易导致“Concurrent Mode Failure”失败,性能反而降低。
  3.最后一个缺点,CMS是基于“标记-清除”算法实现的收集器,使用 “标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来很多麻烦,比如说 大对象,内存空间找不到连续的空间来分配不得不提前触发一次Full  GC。为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关参数,用于在Full  GC之后增加一个内存碎片的合并整理过程,但是内存整理过程是无法并发的,因此解决了空间碎片问题,却使停顿时间变长。还可通过-XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full  GC之后,跟着来一次碎片整理过程(默认值是0,表示每次进入Full GC时都进行碎片整理)。

G1收集器

  G1(Garbage First)收集器是JDK1.7提供的一个新的面向服务端应用的垃圾收集器,其目标就是替换掉JDK1.5发布的CMS收集器。其优点有:
  1.并发与并行:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或CPU核心)来缩短停顿(Stop The World)时间。
  2.分代收集:G1不需要与其他收集器配合就能独立管理整个GC堆,但他能够采用不同方式去处理新建对象和已经存活了一段时间、熬过多次GC的老年代对象以获取更好收集效果。
  3.空间整合:从整体来看是基于“标记-整理”算法实现,从局部(两个Region之间)来看是基于“复制算法实现的,但是都意味着G1运行期间不会产生内存碎片空间,更健康,遇到大对象时,不会因为没有连续空间而进行下一次GC,甚至一次Full GC。
  4.可预测的停顿:降低停顿是G1和CMS共同关注点,但G1除了追求低停顿,还能建立可预测的停顿模型,可以明确地指定在一个长度为M的时间片内,消耗在垃圾收集的时间不超过N毫秒
  5.跨代特性:之前的收集器进行收集的范围都是整个新生代或老年代,而G1扩展到整个Java堆(包括新生代,老年代)。
那么是怎么实现的呢?
  1.如何实现新生代和老年代全范围收集:其实它的Java堆布局就不同于其余收集器,它将整个Java堆划分为多个大小相等的独立区域(Region),仍然保留新生代和老年代的概念,可是不是物理隔离的,都是一部分Region(不需要连续)的集合。
  2.如何建立可预测的停顿时间模型:是因为有了独立区域Region的存在,就避免在Java堆中进行全区域的垃圾收集,G1跟踪各个Region里面的垃圾堆积的价值大小(回收可以获得的空间大小和回收所需要的时间的经验值),后台维护一个优先队列,根据每次允许的收集时间,优先回收价值最大的Region(Garbage-First理念)。因此使用Region划分内存空间以及有优先级的区域回收方式,保证了有限时间获得尽可能高的收集效率。
  3.如何保证垃圾回收真的在Region区域进行而不会扩散到全局:由于Region并不是孤立的,一个Region的对象可以被整个Java堆的任意其余Region的对象所引用,在做可达性判定确定对象是否存活时,仍然会关联到Java堆的任意对象,G1中这种情况特别明显。而以前在别的分代收集里面,新生代规模要比老年代小许多,新生代收集也频繁得多,也会涉及到扫描新生代时也会扫描老年代的情况,相反亦然。解决:G1收集器Region之间的对象引用以及新生代和老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(分代的例子中就检查是否老年代对象引用了新生代的对象),如果是则通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中,当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可避免全堆扫描。
忽略Remembered Set的维护,G1的运行步骤可简单描述为:
①.初始标记(Initial Marking)
②.并发标记(Concurrenr Marking)
③.最终标记(Final Marking)
④.筛选回收(Live Data Counting And Evacution)
1.初始标记:初始标记仅仅标记GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新的对象。这阶段需要停顿线程,不可并行执行,但是时间很短。
2.并发标记:此阶段是从GC Roots开始对堆中对象进行可达性分析,找出存活对象,此阶段时间较长可与用户程序并发执行。
3.最终标记:此阶段是为了修正在并发标记期间因为用户线程继续运行而导致标记产生变动的那一份标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这段时间需要停顿线程,但是可并行执行。
4.筛选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定回收计划。
  如果现有的垃圾收集器没有出现任何问题,没有任何理由去选择G1,如果应用追求低停顿,G1可选择,如果追求吞吐量,和Parallel Scavenge/Parallel Old组合相比G1并没有特别的优势。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值