JVM学习之自动内存管理机制(二)

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

  a、常见判定对象回收算法:

    1、引用计数法:给对象中添加引用计数器,有引用加1,时效时减1,计数值为0则对象时效;

    2、可达性分析法:通过特定的系列GC Roots 节点向下搜索,不可达的对象即为不可用;此种说法中对象一般会被标记两次,首次标记时GC会进行finalIze()方法筛选,这里不建议使用,不做了解。

  补充:jdk1.2之后强化了引用概念:

    强引用:类似代码中赋值“=”的引用

    软引用:指一些有用但非必须的对象。SoftReference类实现,只会在内存即将溢出之前被GC二次回收

    弱引用:描述一些非必须对象,但引用强度弱于软引用。WeakReference类实现,只能存活到下次GC之前。

    虚引用:本机等同于无引用,唯一作用就是在被GC时收到一个系统通知,PhantomReference类实现。

  b、方法区回收机制

    1.废弃常量,方法,符号等:系统无任何引用

    2.无用类:

      可以进行回收的条件:a、所有实例均被回收;b、加载该类的ClassLoader已被回收;c、该类对应的java.lang.Class对象无任何应用,即无法通过任何反射方式访问该类;

      虚拟机堆类回收控制:-Xnoclassgc

    Tip:在大量使用反射,动态代理,CGLib等,动态生成JSP和OSGi这种频繁自定义ClassLoader的场景,都需要虚拟机具备类卸载功能,保证方法区不会溢出

  c、垃圾收集算法

    1、标记-清除算法(基础):先同意标记所有需要回收的算法,最后统一清除。

      缺点:效率问题;空间问题,易产生大量不连续内存碎片;

      

    2、复制算法:等分两份内存,一般内存快满时讲存活对象顺序复制到另一半内存中,清理此内存。很多商业虚拟机都采用此种方法回收新生代。

      缺点:内存牺牲过大

      

    3、标记-整理算法:多用于老年代的回收。过程和标记-清楚一样,但在清楚对象之前,让所有存活对象相一段移动,然后清理边界之外的内存。

       

     4、分代收集:根据对象存活周期将内存划分为新生和老年代,然后根据不同的年代内存选择合适的收集算法

  d、HotSpot虚拟机的算法实现

    1、枚举根节点:如果全局检查,必然会消耗很多时间,但GC工作是必须在一个确保一致性的快照进行,所以在执行GC时必须发生Stop The World事件。主流的虚拟机中都采用准确式GC,引用OopMap的数据结构达到不需全局检查的作用,从而节省时间。

      Tip:类在加载完成时,HotSpot就把对象的对应偏移量上类型的数据计算数来,在JIT编译过程中,在会在特定的位置记录下栈和寄存器中那些位置是引用。这样在GC扫描时就可以直接得知信息。

    2、安全点:因为引用关系的多变,可能导致OopMap内容变化的指令非常多,如果对应生成,那么就需要大量的空间成本。HotSpot设定程序必须到达特定的位置才能暂停,这些位置成为安全点。

      选定标准:是否可以让程序长时间执行。一般的指令都很短,所以基本只有类似方法调用,循环跳转,异常跳转等这些长时间执行的指令才会产生安全点。

      如何在GC时让所有线程到达安全点:

        抢先式:先中断线程,检查是否在安全点上,不在的话就恢复让其跑至安全点。几乎没有虚拟机采用

        主动式:不直接操作线程,设立标志点,线程主动去轮循这个标志,为真是自动中断。

    3、安全区域:为解决程序未执行即未分配到CPU资源时,去响应JVM中断请求,在一段引用关系不会发生变化的代码片段中,这个区域任何地方开始GC都是安全的,即为安全区域。线程进去安全区是进行标记,此时发生GC就不需要等待,只要线程离开安全区域时检查GC是否完成,完成的话就可以继续执行,否则就需要等待信号才可执行。

  e、垃圾收集器

    

     1、Serial收集器

      a、特点:这对新生代;采用复制算法;单线程收集;进行GC时必须暂停所有工作线程,即StopTheWorld;HotSpot在Client模式下默认新生代收集器;简单高效;

       

      b、设置参数:"-XX:+UseSerialGC":添加该参数来显式的使用串行垃圾收集器;

    2、ParNew收集器

      a、特点:除了多线程之外其余行为特点和Serial收集器一样;

      

      b、设置参数:"-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;"-XX:+UseParNewGC":强制指定使用ParNew;"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

    Tips:并行:指多条垃圾收集线程并行工作,但用户线程处于等待状态;并发:指用户和垃圾收集线程同时执行;

    3、Parallel Scavenger 收集器:

      a、特点:新生代收集器,复制算法,并行多线程收集器;可控的吞吐量;

      b、设置参数:-XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间,一个大于0的毫秒数;-XX:GCTimeRatio 直接设置吞吐量大小,一个大于0小于100的整数;-XX:+UseAdaptiveSizePolicy GC自适应调节策略,无需设置-Xmn、-XX:SurvivorRatio、-XX:PretenureSizeThreshold等细节参数。

      Tips:吞吐量=运行用户代码时间/CPU总耗时

    4、Serial Old 收集器

      a、特点:老年代收集器,标记-整理算法,单线程收集器。

        

 

      b、用途:搭配ParallelScavenge收集器使用;作为CMS收集器发生并发ConcurrentModeFailure时的后备预案;

    5、Parallel Old 收集器

      a、特点:ParallelScavenger收集器的老年代版本,多线程,标记-整理算法。

        

       b、设置参数:"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

    6、CMS 收集器

      a、目标:获取最短回收停顿时间

      b、算法:标记-清除

        初始标记:标记GCRoots可以直接关联到的对象,速度快但需要stop the world

        并发标记:GC Roots Tracing

        重新标记:修正期间产生变化的对象标记记录,其中涉及一个叫做浮动垃圾的概念和三色标记法,后续了解。

        并发清除:GC

      c、特点:这对老年代,并发收集,低停顿,耗内存

       

      d、设置参数:"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

      e、缺陷:

        1.对CPU资源非常敏感:CMS默认收集线程数量=(CPU数量+3)/4;会占用部分CPU资源,总吞吐量降低;增量式并发收集器,1.6之后官方不提倡使用,不做了解;

        2.无法处理浮动垃圾和产生ConcurrentModeFailure

          (1)浮动垃圾:并发标记阶段,用户线程新产生的垃圾;也正是因此,CMS是并发工作的,需要预留部分内存空间提供给用户线程,通过-XX:CMSInitiatingOccupancyFraction参数设置老年带内存占用率触发收集器;

          (2)ConcurrentModeFailure失败:内存空间不足时产生,虽然会启用临时SerailOld收集器,但是依旧会产生淋一次FullGC

        3.产生大量内存碎片:算法“标记-清楚”后并未做压缩操作。

          (1)-XX:+UseCMSCompactAtFullCollection:阻止fullGC,开启内存碎片合并整理过程,但会使等待时间变长;

          (2)-XX:+CMSFullGCsBeforeCompaction:设置执行多少次不压缩的full GC后执行一次带压缩的,默认为0,每次都执行FullGC

    7、G1收集器:1.7之后才有,这里是简介,不记录书本上之外的内容,深度了解可以查询相关资料(https://blog.csdn.net/coderlius/article/details/79272773

      a、面向服务端的垃圾收集器:

        1、并行,并发:利用现代机器多核优势缩短STW时间,可以通过并发的方式让需要STW的收集器与用户线程并发执行;

        2、分代收集:基于创新,更好的处理无法GC的对象

        3、空间整合:标记-整理算法。

        4、可预测停顿:建立可预测的停顿时间模型,可以明确在指定的一个时间段内,GC消耗时间不得超过多长时间,这点类似javaRTSJ。

      b、运作步骤:1.初始标记;2.并发标记;3.最终标记;4.筛选回收

        

 

 

    Tips1-理解GC日志:

     

    Tips2-垃圾收集器参数总结:

      

 

        

    Tips3-jvm运行模式:

      client:客户端,不完全编译,编译更快,执行相对慢

      server:服务端,完全编译,启动相对慢,但执行速度快

  f、内存分配与回收策略

    Tips1-MinorGC:新生代GC,指发生在新生代的垃圾收集动作,java对象一般周期很短,所以MinorGC发生非常频繁,回收速度需要很快;

    Tips2-MajorGC/FullGC:老年代GC,经常会伴随至少一次MinorGC,一般会比新生代GC慢十倍以上。

    1、基本内存分配规则:

      a、对象优先在Eden(新生代)分配。

      b、大对象直接进入老年代;大对象指连续内存空间对象;-XX:PretenureSizeThreshold参数设置直接在老年代分配对象,但只对Serial和ParNew收集器有效;

      c、长期存活的对象将进入老年代:-XX:MaxTenuringThreshold设置年龄阀值,使对象年龄计数器达到阀值就可晋升到老年代中。

      d、动态对象年龄判定:某一年龄的对象大小总和大于Survivor空间的一半,那么大于或者等于此年龄的对象都会晋升为老年代。

      e、空间分配担保:就是老年代为新生代进行空间担保,复制收集算法为了提高内存利用率只有一块Survivor空间备用,所以在MinorGC之前要确保老年代有足够的内存存放Survivor无法储存的对象。每次MinorGC能存活多少对象无法明确,所以一般取之前晋升老年代的均值作为担保标准。所以这个还是很有一定几率产生失败!失败后还是发起一次FullGC。jdk1.6之后HandPromotionFailure参数不影响分配担保策略,基本已经不使用。

 

总结:垃圾算法,垃圾收集器特点及运作原理大概了解了虚拟机的自动内存分配回收的规则。

      

posted @ 2019-09-06 19:01  鲁尐肅  阅读(109)  评论(0)    收藏  举报