• GC调优


    Reference:https://time.geekbang.org/column/article/107396

    GC算法

    JVM提供了不同的回收算法来实现这⼀套回收机制,通常垃圾收集器的回收算法可以分为以下⼏种:

    如果说收集算法是内存回收的⽅法论,那么垃圾收集器就是内存回收的具体实现,JDK1.7 update14 之后Hotspot虚拟机所有的回收器整理如下(以下为服务端垃圾收集器):

    其实在JVM规范中并没有明确GC的运作⽅式,各个⼚商可以采⽤不同的⽅式实现垃圾收集器。我们可以通过JVM⼯具查询当前JVM使⽤的垃圾收集器类型,⾸先通过ps命令查询出经常ID,再通过jmap -heap ID查询出JVM的配置信息,其中就包括垃圾收集器的设置类型。 

    GC性能衡量指标

    ⼀个垃圾收集器在不同场景下表现出的性能也不⼀样,那么如何评价⼀个垃圾收集器的性能好坏呢?我们可以借助⼀些指标。

    吞吐量这⾥的吞吐量是指应⽤程序所花费的时间和系统总运⾏时间的⽐值。我们可以按照这个公式来计算GC的吞吐量:系统总运⾏时间=应⽤程序耗时+GC耗时。如果系统运⾏了100分钟,GC耗时1分钟,则系统吞吐量为99%。GC的吞吐量⼀般不能低于95%。

    停顿时间指垃圾收集器正在运⾏时,应⽤程序的暂停时间。对于串⾏回收器而言,停顿时间可能会⽐较长;而使⽤并发回收器,由于垃圾收集器和应⽤程序交替运⾏,程序的停顿时间就会变短,但其效率很可能不如独占垃圾收集器,系统的吞吐量也很可能会降低。

    垃圾回收频率:多久发⽣⼀次指垃圾回收呢?通常垃圾回收的频率越低越好,增⼤堆内存空间可以有效降低垃圾回收发⽣的频率,但同时也意味着堆积的回收对象越多,最终也会增加回收时的停顿时间。所以我们只要适当地增⼤堆内存空间,保证正常的垃圾回收频率即可。

    GC调优策略

    找出问题后,就可以进⾏调优了,下⾯介绍⼏种常⽤的GC调优策略。

    1. 降低Minor GC频率

    通常情况下,由于新⽣代空间较⼩,Eden区很快被填满,就会导致频繁Minor GC,因此我们可以通过增⼤新⽣代空间来降低Minor GC的频率。

    可能你会有这样的疑问,扩容Eden区虽然可以减少Minor GC的次数,但不会增加单次Minor GC的时间吗?如果单次Minor GC的时间增加,那也很难达到我们期待的优化效果呀。

    我们知道,单次Minor GC时间是由两部分组成:T1(扫描新⽣代)和T2(复制存活对象)。

    假设⼀个对象在Eden区的存活时间为500ms,Minor GC的时间间隔是300ms,那么正常情况下,Minor GC的时间为 :T1+T2。

    当我们增⼤新⽣代空间,Minor GC的时间间隔可能会扩⼤到600ms,此时⼀个存活500ms的对象就会在Eden区中被回收掉,此时就不存在复制存活对象了,所以再发⽣Minor GC的时间为:两次扫描新⽣代,即2T1。

    可见,扩容后,Minor GC时增加了T1,但省去了T2的时间。通常在虚拟机中,复制对象的成本要远⾼于扫描成本。

    如果在堆内存中存在较多的长期存活的对象,此时增加年轻代空间,反⽽会增加Minor GC的时间。如果堆中的短期对象很多,那么扩容新⽣代,单次Minor GC时间不会显著增加。因此,单次Minor GC时间更多取决于GC后存活对象的数量,⽽⾮Eden区的大小

    2. 降低Full GC的频率

    通常情况下,由于堆内存空间不足或老年代对象太多,会触发Full GC,频繁的Full GC会带来上下⽂切换,增加系统的性能开销。我们可以使用哪些⽅法来降低Full GC的频率呢?

    减少创建⼤对象:在平常的业务场景中,我们习惯⼀次性从数据库中查询出⼀个大对象⽤于web端显⽰。例如,我之前碰到过⼀个⼀次性查询出60个字段的业务操作,这种大对象如果超过年轻代最大对象阈值,会被直接创建在⽼年代;即使被创建在了年轻代,由于年轻代的内存空间有限,通过Minor GC之后也会进⼊到⽼年代。这种大对象很容易产⽣较多的Full GC。

    我们可以将这种大对象拆解出来,首次只查询⼀些⽐较重要的字段,如果还需要其它字段辅助查看,再通过第⼆次查询显示剩余的字段。

    增大堆内存空间:在堆内存不足的情况下,增大堆内存空间,且设置初始化堆内存为最大堆内存,也可以降低Full GC的频率。

    选择合适的GC回收器

    假设我们有这样⼀个需求,要求每次操作的响应时间必须在500ms以内。这个时候我们⼀般会选择响应速度较快的GC回收器,CMS(Concurrent Mark Sweep)回收器和G1回收器都是不错的选择。

    而当我们的需求对系统吞吐量有要求时,就可以选择Parallel Scavenge回收器来提⾼系统的吞吐量。

    总结

    垃圾收集器的种类很多,我们可以将其分成两种类型

    • 响应速度快
    • 吞吐量⾼

    通常情况下,CMS和G1回收器的响应速度快,Parallel Scavenge回收器的吞吐量⾼

    在JDK1.8环境下,默认使⽤的是Parallel Scavenge(年轻代)+ Serial Old(⽼年代)垃圾收集器。

    通常情况,JVM是默认垃圾回收优化的,在没有性能衡量标准的前提下,尽量避免修改GC的⼀些性能配置参数。如果⼀定要改,那就必须基于⼤量的测试结果或线上的具体性能来进⾏调整。

  • 相关阅读:
    linux --- mysql --- max_allowed_packet
    idea 快捷键
    TypedArray和obtainStyledAttributes使用
    ubuntu中怎样添加或删除一个PPA源
    Ubuntu 14.04 用户安装 Cinnamon 2.2.0
    android-pulltorefresh源码解析(1)--PullToRefreshListView的使用
    Android菜单详解(四)——使用上下文菜单ContextMenu
    Android菜单详解(五)——使用XML生成菜单
    Android菜单详解(二)——创建并响应选项菜单
    Android菜单详解(三)——SubMenu和IconMenu
  • 原文地址:https://www.cnblogs.com/agilestyle/p/11488008.html
Copyright © 2020-2023  润新知