JVM垃圾收集器与内存分配策略

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

一、如何判断对象是否还在存活

二、引用:

三、对象标记之后就会回收吗

四、方法区的回收

五、垃圾收集算法

          

     1、标记-清除算法:

          首先是标记出所有需要回收的对象, 在标记完成后回收所有被标记的对象。

          

          缺点:

      2、复制算法:

          

          描述:将可用内存按容量划分为两块, 每次只是用其中的一块, 当这一块内存用完了, 就将还存活的对象复制到另外一块内存, 然后再把已使用过的内存空间一次性的清理掉。

          优点:不用考虑内存碎片

          缺点:内存缩小为原来的一半

          应用:目前的商业虚拟机都采用这种收集算法来回收新生代,具体如下:

                    将内存分为一块较大的Eden空间和两块较小的Survivior空间, 每次使用Eden和其中一个Survivior空间。 
                    当回收时, 将Eden区和Survivior中还存活着的对象一次性的复制到另外一块Survivior空间上, 最后清理Eden区和另一块Survivior区。

                    Hotspot 虚拟机默认Eden和Survivior的大小比例是8:1

                    当另外一块Survivior空间没有足够的空间存放上一次新生代存活的对象时, 这些对象将直接通过分配担保机制进入老年代。


     3、标记-整理算法:

          

          标记之后, 让所有存活的对象都向一端移动, 然后直接清理掉端边界以外的内存。


     4、分代收集算法:


          当前虚拟机的垃圾收集都采用”分代收集“算法。一般是把Java堆分为新生代和老年代;

          新生代: 每次垃圾收集时都会有大批对象死去, 只有少量存活, 该区域采用复制算法。

          老年代:对象存活率较高, 而且没有额外空间对她进行分配担保, 就必须使用”标记-清理“或者”标记-整理“算法进行回收。


六、HotSpot 的算法


      1、枚举根节点:


          GC停顿:
               可达性分析要确保在一个一致性的快照中进行, 确保分析过程中引用关系不再变化。 GC进行时, 必须停顿所有的Java执行线程。 Stop the World

          如何枚举根节点:

               GC Roots主要在全局性的引用, 如常量、类静态属性, 与执行上下文中, 如果逐个检查, 必然消耗大量的时间。


               使用OopMap: HotSpot使用一组OopMap来记录对象的引用, 加快GC Roots的枚举。


     2、安全点:

          

          背景:          如果每个指令都生成对应的OopMap, 那会需要大量的额外空间, 这样GC的成本将会变得非常高。

          解决办法:    只在特定位置记录对象的引用情况, 这些特点的位置我们称之为安全点。

          安全点的选定条件:是否具有让程序长时间执行的指令 (原因)

          如何保证GC时, 让所有线程都跑到最近的安全点上再停顿下来:

      3、安全区域:


          背景: 安全点机制保证了程序执行时, 在不太长时间就会遇到可进入GC的安全点, 如果程序没有执行呢, 比如出于sleep或者blocked状态,
                    这时候线程无法响应jvm的中断请求, Jvm也显然不太可能等待线程被重新分配CPU时间。

          安全区域:在一段代码之中, 引用关系不会发生变化, 在这个区域中任意地方开始GC都是安全的

          实现: 当线程执行到安全区域后, 首先会标示自己进入了安全区域, 那样, 当在这段时间内发生GC时, 就不用管这样的线程了,当线程要离开
                    该区域时, 要检查系统是否已经完成了根节点枚举, 如果没完成, 它就必须等待直到收到可以安全离开安全区域的信号。


七,垃圾收集器


          目前新生代垃圾收集器有Serial,ParNew,Parallel Scavenge; 老年代收集器有CMS,Serial Old,Parallel Old;G1这款垃圾收集器既能用于新生代又能用于老年代。


     1,Serial收集器:


          描述:     单线程收集器;他进行垃圾收集时,必须暂停所有的工作线程, 直到它收集结束;新生代采取复制算法暂停所有用户线程, 老年代采取标记-整理算法暂停所有用户线程。

          现状:     目前为止, 依然是虚拟机运行在Client模式下的默认新生代收集器。收集几十兆甚至一两百兆的新生代, 停顿时间完全可以控制在几十毫秒最多一百毫秒以内。


     2,ParNew收集器:


          描述:     其实就是Serial的多线程版本;使用多线程进行垃圾收集;新生代采取复制算法暂停所有用户线程,老年代采用标记-整理算法暂停所有用户线程。

          现状:     许多运行在Server模式下的虚拟机默认的新生代收集器;一个与性能无关的原因是:除了Serial收集器外, 目前只有它能与CMS收集器配合使用。

          注:

     3,Parallel Scavenge收集器:


          描述:     Parallel Scavenge收集器的目标是达到一个可控制的吞吐量。所谓吞吐量就是cpu用于运行用户代码的时间与CPU总消耗时间的比值, 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

          参数:     最大垃圾收集停顿时间设置:-XX:MaxGCPauseMillis, 设置值是一个大于0的毫秒数, 收集器将尽可能地保证内存回收花费的时间不超过设定值。

                         GC停顿时间的缩短是以牺牲吞吐量和新生代空间来换取的,可能会把新生代调小一些, 以使在规定的时间内可以完成垃圾回收; 也可能为了减小停顿时间而增大GC频率。

                         -XX:+GCTimeRatio:设置一个大于0且小于100的整数值, 也就是垃圾收集时间占总时间的比率,如果设置成x, GC时间的占比就是 1/(1+x)

                         -XX:+UseAdaptiveSizePolicy: 这是一个开关参数, 打开这个参数后, 不需要手工指定新生代大小,Eden和Survior区的比例,晋升老年代对象年龄等细节参数。 虚拟机会根据当前系统的运行情况动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。


          与ParNew收集器的区别:可以设置吞吐量和最大停顿时间; 具有自适应调节策略。


     4,Serial Old收集器:


          描述:Serial Old是Serial收集器的老年代版本, 单线程收集器, 使用“标记-整理”算法。


     5,Parallel Old收集器:


          描述: Parallel Scavenge收集器的老年代版本;吞吐量优先的收集器


     6,CMS收集器: Concurrent Mark Sweep

     

          描述:以获取最短回收停顿时间为目标;基于“标记-清除”算法

          步骤:

          缺点:

八、 理解GC日志


          <img src="http://jishu.family.baidu.com/portal/lib/tpubbs/ueditor/themes/default/images/spacer.gif" no-repeat="" center="" center;border:1px="" solid="" #ddd"="" style="border: 0px; display: block;">

  1. 停顿类型:
    [GC : minor GC, [Full GC: full GC

  2.  GC的位置:
    [DefNew:Default New Generation      Serial收集器新生代
    [ParNew:  Parallel 新生代
    [PSYoungGen: Parallel Scanvenge收集器的新生代
    [Tenured:   老年代
    [Perm:  永久代

  3. 回收前后内存空间变化:
    35592K -> 1814K(36288K): 回收前内存空间大小 -> 回收后内存空间大小(总的内存空间大小

九、内存分配与回收策略:


     大的方向说, 对象主要分配在堆的新生代的Eden区上,如果启动了本地线程分配缓冲, 将按线程优先在TLAB上分配。

  1. 对象优先在Eden上分配:
    当Eden上没有足够的空间分配时, 虚拟机会发起一次Minor GC, 将Eden上和一个survivior上存活的对象复制到另外一个Survivor空间上, 如果另外一个Survivior空间上没有足够的空间, 将会将存活的对象直接移动到老年代, 如果老年代也没有足够的空间, 虚拟机将会发起一次Full GC, 如果Full GC之后还是放不下, 则会报OOM异常。

  2. 大对象会直接进入老年代:
    虚拟机提供一个参数, -XX:PretenureSizeThreshould, 大于这个值的对象直接在老年代分配, 避免Eden区域Survivior区的来回复制。

  3. 长期存活的对象直接进入老年代:
    虚拟机每个对象定义了一个对象年龄计算器, 每经过一次Minor GC, 对象年龄加一, 当对象年龄达到一定数时(默认15),将会晋升到老年代。 阈值设置参数:-XX:MaxTenurngThreshould

  4. 动态对象年龄判断:
    如果Survivior空间中相同年龄的对象的大小总和大于survivior空间的一半时, 大于等于该年龄的对象就可以直接进入老年代。

  5. 空间分配担保:
    准备Minor GC时, 虚拟机首先会检查老年代的剩余空间是否大于新生代所有对象的总空间, 如果大于, 则可以进行Minor GC; 否则, 会去查看是否允许担保失败(HandlePromotionFailure), 如果不允许,虚拟机会直接发起一次Full GC; 如果允许, 虚拟机会去检查老年代剩余空间是否大于历次晋升到老年代对象的平均大小, 如果不大于, 则会发起一次Full GC; 如果大于, 则会发起Minor GC, 如果这时发现, 老年代没有足够空间来容纳新生代晋升来的对象的总大小, 这时仍要触发一次Full GC, 这个圈子绕的就有点大了。

十、Full GC 和Minor GC:


     Minor GC: 发生在新生代, 速度比较快
     Full GC: 发生在老年代, 一般都伴随这一次Minor GC

     

     Minor GC一般都要比Minor GC慢十倍以上,因为新生代采用复制算法, 速度比较快; 而老年代一般采用标记-清理/整理算法;


关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看