jvm优化垃圾回收算法和垃圾收集器实现

1、什么是垃圾回收

程序的运行必然需要申请内存资源,无效的对象如果不及时清理就会一直占用内存资源,此时无效对象已成为垃圾,需要回收,垃圾回收就是对内存资源进行管理。java语言依赖虚拟机自动对垃圾进行回收。

2、垃圾回收常见算法

自动化的内存资源管理必定需要一套算法来进行计算,哪些是有效的对象,哪些是无效的对象。
常见的垃圾回收算法有引用计数法,标记清除,标记压缩,复制算法,分代算法等。
2.1 引用计数法
引用计数法是历史最悠久的一种算法,直到今天依然被很多编程语言使用。

  • 优点
    实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
    在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
    区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
  • 缺点
    无法解决循环引用问题。
    2.2 标记清除法
    标记清除算法是将垃圾回收分为两个阶段,分别是标记和清除。
  • 标记,从根节点开始标记引用的对象。
  • 清除,未被标记的对象就是垃圾对象,可以清除。
    优缺点:
    解决了循环引用的问题,没有从root节点引用的对象都会被回收。
    缺点:效率低。标记和清除两步都要遍历所有对象,并且需要STW。
    标记清除出来的内存,碎片化比较严重。
    在这里插入图片描述

2.3 标记压缩算法
标记压缩算法对标记清除算法做了改进。在标记阶段也是和根节点开始,对对象的引用进行标记,在清理阶段并不是简单的清理,而是把存活对象压缩到内存一端,解决碎片化问题。
在这里插入图片描述
2.4 复制算法
复制算法的核心就是将内存一分为二,每次只使用其中一块,垃圾回收时,将仍然存活的对象复制到另一块内存,然后将内存空间清理。适用于需要清理的垃圾对象较多的情形,否则需要复制的对象太多。
在这里插入图片描述
2.5 分代算法
在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法。

3、垃圾收集器以及内存分配

有了垃圾回收算法,还需要有具体的实现,在jvm中实现了:串行垃圾收集器、并行垃圾收集器、CMS(并发)垃圾收集器、G1垃圾收集器。
3.1 串行垃圾收集器
串行垃圾收集器指的是使用单线程进行垃圾回收,垃圾回收时只有一个线程在工作,并且java应用中所有线程都要暂停等待垃圾回收完成。STW
一般交互性强的应用不会采用。

  • -XX:+UseSerialGC
    指定年轻代和老年代都使用串行垃圾收集器
  • -XX:+PrintGCDetails
    打印垃圾回收的详细信息
    示例:-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m
    在这里插入图片描述
    3.2 并行垃圾收集器
    并行垃圾收集器对串行垃圾收集器做了改进,将单线程改为多线程进行垃圾回收,垃圾回收过程中也会暂停应用程序,只是速度更快,暂停时间更短一些。
    3.2.1 ParNew垃圾收集器
    ParNew垃圾收集器工作在年轻代,只是将串行的垃圾回收改成并行。通过-XX:+UseParNewGC参数设置年轻代使用ParNew回收器,老年代使用的依然是串行收集器。
    在这里插入图片描述
    3.2.2 ParallelGC垃圾收集器
    ParallelGC收集器工作机制和ParNewGC收集器一样,只是在此基础之上,新增了两个和系统吞吐量相关的参数,使得其使用起来更加的灵活和高效。
  • -XX:+UseParallelGC
    年轻代使用ParallelGC垃圾回收器,老年代使用串行回收器。
  • -XX:+UseParallelOldGC
    年轻代使用ParallelGC垃圾回收器,老年代使用ParallelOldGC垃圾回收器。
  • -XX:MaxGCPauseMillis
    设置最大的垃圾收集时的停顿时间,单位为毫秒
    需要注意的时,ParallelGC为了达到设置的停顿时间,可能会调整堆大小或其他的参数,如果堆的大小
    设置的较小,就会导致GC工作变得很频繁,反而可能会影响到性能。该参数使用需谨慎。
  • -XX:GCTimeRatio
    设置垃圾回收时间占程序运行时间的百分比,公式为1/(1+n)。它的值为0~100之间的数字,默认值为99,也就是垃圾回收时间不能超过1%
  • -XX:UseAdaptiveSizePolicy
    自适应GC模式,垃圾回收器将自动调整年轻代、老年代等参数,达到吞吐量、堆大小、停顿时间之间的平衡。一般用于,手动调整参数比较困难的场景,让收集器自动进行调整。
    在这里插入图片描述
    3.3 CMS垃圾收集器
    CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,用于老年代的垃圾回收。通过参数-XX:+UseConcMarkSweepGC进行设置。
    CMS垃圾收集器过程如下:
    在这里插入图片描述
  • 初始化标记,标记root,会导致STW
  • 并发标记,与用户线程同时进行
  • 预处理,与用户线程同时进行
  • 重新标记,会导致STW
  • 并发清理,与用户线程同时进行
  • 调整堆大小,CMS在清理会后进行内存压缩,目的是清理内存中的碎片
  • 重置,等待下次CMS的触发,与用户线程同时进行
    设置启动参数:-XX:+UseConcMarkSweepGC
    在这里插入图片描述
    3.4 G1垃圾收集器
    G1是jdk1.7开始正式使用的全新垃圾收集器,oracle官网计划jdk1.9将G1变成默认垃圾收集器。
    G1的设计原则就是简化内存调优:
  • 开启G1收集器
  • 设置对内存大小
  • 设置最大停顿时间
    G1提供了三种垃圾收集模式,Young GC,
    Mixed GC,Full GC。
    3.4.1 原理
    G1垃圾收集器和其他垃圾收集器的最大不同,取消了年轻代、老年代在物理上的划分,取而代之的是将堆分成若干个区域,这些区域包含了有逻辑上的年轻代、老年代。
    在这里插入图片描述
    在G1划分的区域中,年轻代的垃圾收集器依然采用了暂停所有用户线程的方式,将存活对象拷贝到老年代或者Survivor区,G1通过将对象从一个区域复制到另一个区域完成了清理工作,这也意味着在正常的清理过程中,G1完成了部分堆的压缩工作。
    G1中有个特殊区域,叫Humongous区域。
  • 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。
  • 这些巨型对象,默认直接会被分配在老年代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。
  • 为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。
    3.4.2 Young GC
    Young GC主要对Eden去进行GC,他在Eden空间耗尽时触发。
  • Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代
    空间。
  • Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。
  • 最终Eden空间的数据为空,GC停止工作,应用线程继续执行。
    在这里插入图片描述
    在这里插入图片描述
    Remembered Set(已记忆集合)
    根对象可能存在于年轻代中也可能存在于老年代中,如果要对对象进行全量扫描会话费比较多的时间。G1引进了Remembered Set的概念。作用是跟踪指向某个堆内对象的引用。
    3.4.3 Mixed GC
    当越来越多的对象晋升到老年代时,为了避免堆内存耗尽,虚拟机会触发Mixed GC,该算法除了回收年轻代还会回收一部分老年代对象。触发的时机由参数由 -XX:InitiatingHeapOccupancyPercent=n 决定。默认:45%,意思是老年代大小占整个堆内存大小达到一定阈值时触发。
    分为两步:
  • 全局并发标记
    初始标记(initial mark,STW)
    根区域扫描(root region scan)
    并发标记(Concurrent Marking)
    重新标记(Remark,STW)
    清除垃圾(Cleanup,STW)
  • 拷贝存活对象
    Evacuation阶段是全暂停的,通过把一部分Region中对象拷贝到另一部分Region中,实现垃圾回收。
    3.4.4 G1垃圾回收器相关参数
  • -XX:+UseG1GC
    使用G1垃圾收集器
  • -XX:MaxGCPauseMillis
    设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到),默认值是 200 毫秒。
  • -XX:G1HeapRegionSize=n
    设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。目标是根据最小的 Java 堆大小划
    分出约 2048 个区域。
    默认是堆内存的1/2000。
  • -XX:ParallelGCThreads=n
    设置 STW 工作线程数的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,最多为 8。
  • -XX:ConcGCThreads=n
    设置并行标记的线程数。将 n 设置为并行垃圾回收线程数 (ParallelGCThreads) 的 1/4 左右。
  • -XX:InitiatingHeapOccupancyPercent=n
    设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。
    示例:-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m
    注意点:
    不要显式设置年轻代大小。
    暂停时间不要太苛刻。
    G1 GC 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间。

4、可视化日志分析工具

可以使用GC Easy工具进行分析。地址:
http://gceasy.io/
需要将GC日志打印到文件中,使用第三方工具进行分析。

  • -XX:+PrintGC 输出GC日志
  • -XX:+PrintGCDetails 输出GC的详细日志
  • -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
  • -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
  • -Xloggc:…/logs/gc.log 日志文件的输出路径
    示例:
    -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m
    -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
    -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
    -Xloggc:D://g1.log

    可以与CMS垃圾回收器进行比较:
    -XX:+UseConcMarkSweepGC -Xmx256m
    -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
    -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
    -Xloggc:D://cms.log
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值