zoukankan      html  css  js  c++  java
  • 深入理解Java虚拟机—垃圾回收 下

    垃圾收集算法

    垃圾收集算法的实现涉及大量的程序细节
    而且各个平台的虚拟机操作内存的方法又各不相同
    因此,书中只介绍了收集算法的思想和发展过程

    1.标记-清除算法

    最基础的收集算法“标记-清除”算法
    分为两个阶段:标记和清除
    首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象

    它的主要缺点有两个:
    一个是效率问题,标记和清除过程的效率都不高
    另一个是空间问题,标记清除后产生大量不连续的内存碎片

    2.复制算法

    为了解决效率问题,一种称为“复制”的收集算法出现了
    它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块
    当这一块的内存用完了,就将还存活着的对象复制到另一块上面
    然后再把已使用过的内存空间一次清理掉

    举个例子来说,大概就是:
    如一块4G的堆内存,复制算法将其均分为两部分,各2G,分别为A和B
    假设初始时,A作为内存分配区域,B作为保留区域
    分配内存时只在A中进行分配,若是A的内存已满,那么将A中还存活的对象按顺序复制到B中
    然后将A中的内存一次全部清理掉,此时A作为保留区域,B作为内存分配区域
    如此循环往复下去

    这种算法实现简单,运行高效,并且不用考虑内存碎片等复杂情况
    但需要将内存缩小为原来的一半来使用,代价似乎太大

    这种算法非常适合于新生代,因为据IBM的研究表明:新生代中的对象98%是朝生夕死的
    对象存活率低时,复制收集算法在执行时的复制操作就少,效率自然也就高了起来

    3.标记-整理算法

    但复制算法不适用于老年代,老年代的存活率比较高
    于是,根据老年代的特点,有人提出了另一种“标记-整理”算法
    标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理
    而是让所有存活的对象都想向一端移动,然后直接清理端边界以外的内存

    4.分代收集算法

    当前虚拟机的垃圾收集都采用“分代收集”算法,这种算法并没有什么新的思想
    只是根据对象的存活周期的不同将内存分为几块
    一般是把Java堆分为新生代和老年代,这样可以根据各个年代的特点采用最适当的收集算法

    在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活
    那就选用复制算法,只需付出少量存活对象的复制成本就可以完成收集

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

    垃圾收集器

    如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
    Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定
    因此不同的厂商,不同版本的虚拟机所提供的垃圾收集器都可能会有很大的差别

    书中关于垃圾收集器的讨论基于Sun HotSpot虚拟机1.6版Update 22
    在这之前,先介绍两个有关的概念:
    并行(Parrallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
    并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行)
    用户程序继续运行,而垃圾收集器程序运行于另一个CPU上

    1.Serial收集器

    又称串行收集器,是一个单线程的收集器
    它的“单线程”的意义并不仅仅是说明他只会使用一个CPU或者一条收集线程去完成垃圾收集工作
    更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程(Sun 称之为“Stop The World”),直到它收集结束

    Serial收集器是虚拟机运行在Client模式下的默认新生代收集器
    它也有着优于其它收集器的地方:简单而高效
    Serial/Serial Old的运行过程

    2.ParNew收集器

    ParNew收集器其实就是Serial收集器的多线程版本
    除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World
    对象分配规则,回收策略等都与Serial收集器完全一样

    ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器
    其中一个很重要的原因是除了Serial收集器外,目前只有它能与CMS收集器配合工作
    ParNew收集器的工作过程

    3.Parallel Scavenge收集器

    Parallel Scavenge收集器是一个新生代收集器,它使用复制算法,是并行的多线程收集器
    Parallel Scavenge收集器的特别之处在于它的关注点与其他收集器不同
    CMS等收集器的关注点尽可能地缩短垃圾收集时的停顿时间
    Parallel Scavenge的目标则是是达到一个可控的吞吐量
    所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值
    虚拟机总运行100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%

    4. Serial Old收集器

    Serial Old是Serial的老年版本,单线程收集器,使用“标记-整理”算法
    这个收集器的主要意义也是被Client模式下的虚拟机使用

    5. Parallel Old收集器

    Parallel Old是Parallel Scavenge收集器的老年版本
    使用多线程和“标记-整理”算法

    6.CMS收集器

    CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
    基于“标记-清除”算法实现,它的运作工程相比于前面几种回收器来说更复杂一些
    整个过程分为4个步骤:初始标记、并发标记、重新标记、并发清除
    其中初始标记、重新标记这两个步骤仍然需要"Stop The World"
    初始标记仅仅只是标记下GC Roots能直接关联到的对象,速度很快
    并发标记阶段就是进行GC Roots Tracing的过程
    重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录

    由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程可以和用户线程一起工作
    所以总体上来说,CMS收集器的内存回收过程始于用户线程一起并发地执行的
    即做到了在你妈打扫房间的时候你还能同时往地上扔纸屑

    CMS是一款优秀的收集器,它的主要优点已经在名字上体现出来了:
    并发收集、低停顿
    但有三个显著的缺点:

    1. CMS收集器对CPU资源非常敏感
    2. CMS收集器无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致另一次Full GC的产生
    3. 基于标记-清除算法,会产生大量空间碎片

    7.G1收集器

    G1(Garbage First)收集器是当前收集器技术发展的最前沿结果
    基于“标记整理”算法实现,可以非常精确地控制停顿

    8.垃圾收集器参数总结


    通过CMD的java -XX:+PrintCommandLineFlags命令可以查看安装的JVM的参数

    可以看出 64位的JVM1.8的Server模式下的使用的是UseParallelGC
    即使用Parallel Scavenge+Serial Old(PS MarkSweep)的收集器组合来进行内存回收

  • 相关阅读:
    11.爱吃皮蛋的小明(斐波那契数列)
    10.二叉树最大宽度和高度
    9.二叉树的序遍历
    8.递归第一次
    6.数的计算(递归算法)
    5.十进制转m进制
    数论——快速幂(C++)
    最短路径——SPFA算法(C++)
    最小环——Floyd变种算法(C++)
    数论——质因数分解(C++)
  • 原文地址:https://www.cnblogs.com/ASE265/p/12773930.html
Copyright © 2011-2022 走看看