垃圾收集器

串行、并行、并发

  • 串行:一个GC线程运行
    在这里插入图片描述
  • 并行:多个GC线程同时运行
    在这里插入图片描述
  • 并发:多个GC线程与多个用户线程同时运行
    在这里插入图片描述

GC常用参数

  • -Xmn -Xms -Xmx -Xss
    年轻代 最小堆 最大堆 栈空间
  • -XX:+UseTLAB
    使用TLAB,默认打开
  • -XX:+PrintTLAB
    打印TLAB的使用情况
  • -XX:TLABSize
    设置TLAB大小
  • -XX:+DisableExplictGC
    System.gc()不管用 ,FGC
  • -XX:+PrintGC 输出GC日志
  • -XX:+PrintGCDetails 输出GC的详细日志
  • -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
  • -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
  • -XX:+PrintGCApplicationConcurrentTime (低)
    打印应用程序时间
  • -XX:+PrintGCApplicationStoppedTime (低)
    打印暂停时长
  • -XX:+PrintReferenceGC (重要性低)
    记录回收了多少种不同引用类型的引用
  • -verbose:class
    类加载详细过程
  • -XX:+PrintVMOptions
  • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
    必须会用
  • -Xloggc:opt/log/gc.log
  • -XX:MaxTenuringThreshold
    升代年龄,最大值15
  • 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
    这些不建议设置

STW

Stop The World。即GC线程与用户线程无法并发运行,GC线程执行期间需要暂停用户线程。
比如:你妈给你打扫房间,需要把你从房间赶出去,不然她一般打扫垃圾,你一边制造垃圾,没完没了了。那接下来可能要家法伺候了

垃圾收集器

在这里插入图片描述

Serial收集器

串行
串行垃圾收集器,即GC线程与用户线程先后运行,即GC时需要STW(暂停所有用户线程),直至GC结束才恢复用户线程的运行

  • JDK1.5前的默认算法
  • 缺点是只有一个线程,执行垃圾回收时程序停止的时间比较长
  • 语法
    -XX:+UseSerialGC
    新生代、老年代使用串行回收
    新生代复制算法
    老年代标记-压缩
    在这里插入图片描述
    -Xmx20m -Xms20m -Xmn2m -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
public class UseSerialGC {
    public static void main(String[] args) {
        byte[] b = null;
        for (int i = 0; i < 7; i++)
        {
            b = new byte[3 * 1024 * 1024];
        }
    }
}

串行的关键字是DefNew(年轻代)、Tenured(年老代)、Metaspace(元空间)

0.114: [GC (Allocation Failure) 0.114: [DefNew: 1663K->191K(1856K), 0.0012588 secs] 1663K->749K(20288K), 0.0012941 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.133: [GC (Allocation Failure) 0.133: [DefNew: 813K->79K(1856K), 0.0014593 secs]0.135: [Tenured: 16107K->3898K(18432K), 0.0028484 secs] 16730K->3898K(20288K), [Metaspace: 3269K->3269K(1056768K)], 0.0043565 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 1856K, used 17K [0x00000000fec00000, 0x00000000fee00000, 0x00000000fee00000)
  eden space 1664K,   1% used [0x00000000fec00000, 0x00000000fec04400, 0x00000000feda0000)
  from space 192K,   0% used [0x00000000feda0000, 0x00000000feda0000, 0x00000000fedd0000)
  to   space 192K,   0% used [0x00000000fedd0000, 0x00000000fedd0000, 0x00000000fee00000)
 tenured generation   total 18432K, used 10042K [0x00000000fee00000, 0x0000000100000000, 0x0000000100000000)
   the space 18432K,  54% used [0x00000000fee00000, 0x00000000ff7ce9c8, 0x00000000ff7cea00, 0x0000000100000000)
 Metaspace       used 3276K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 357K, capacity 388K, committed 512K, reserved 1048576K
ParNew收集器

erial收集器的多线程版本。唯一能与CMS收集器搭配使用的新生代收集器。

  • 相关参数:
    -XX:+UseConcMarkSweepGC:指定使用CMS后,会默认使用ParNew作为新生代收集器
    -XX:+UseParNewGC:强制指定使用ParNew
    -XX:ParallelGCThreads:指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同
    在这里插入图片描述
    -Xmx20m -Xms20m -Xmn2m -XX:+UseParNewGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
public class ParNewGC {
    public static void main(String[] args) {
        byte[] b = null;
        for (int i = 0; i < 7; i++)
        {
            b = new byte[3 * 1024 * 1024];
        }
    }
}
0.120: [GC (Allocation Failure) 0.120: [ParNew: 1663K->192K(1856K), 0.0008458 secs] 1663K->797K(20288K), 0.0008824 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.141: [GC (Allocation Failure) 0.141: [ParNew: 801K->192K(1856K), 0.0004771 secs]0.141: [Tenured: 16198K->3935K(18432K), 0.0033519 secs] 16767K->3935K(20288K), [Metaspace: 3269K->3269K(1056768K)], 0.0038799 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 1856K, used 17K [0x00000000fec00000, 0x00000000fee00000, 0x00000000fee00000)
  eden space 1664K,   1% used [0x00000000fec00000, 0x00000000fec04400, 0x00000000feda0000)
  from space 192K,   0% used [0x00000000feda0000, 0x00000000feda0000, 0x00000000fedd0000)
  to   space 192K,   0% used [0x00000000fedd0000, 0x00000000fedd0000, 0x00000000fee00000)
 tenured generation   total 18432K, used 10080K [0x00000000fee00000, 0x0000000100000000, 0x0000000100000000)
   the space 18432K,  54% used [0x00000000fee00000, 0x00000000ff7d8008, 0x00000000ff7d8200, 0x0000000100000000)
 Metaspace       used 3276K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 357K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

Process finished with exit code 0

Parallel收集器
  • 关注吞吐量的收集器
    吞吐量 = 运行用户代码时间 / (运行用户代码时间+垃圾收集时间)

  • 相关参数:

    • -XX:MaxGCPauseMillis:是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值。不过大家不要异想天开地认为如果把这个参数的值设置得稍小一点就能使得系统的垃圾收集速度变得更快,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的:系统把新生代调小一些,收集300MB新生代肯定比收集500MB快吧,这也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒,现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降,但吞吐量也降下来了。
    • -XX:GCTimeRatio:一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率。如果把此参数设置为19,那允许的最大GC时间就占总时间的5%(即1 /(1+19)),默认值为99,就是允许最大1%(即1 /(1+99))的垃圾收集时间。
    • -XX:+UseAdaptiveSizePolicy:一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)

-Xmx20m -Xms20m -Xmn2m -XX:GCTimeRatio=90 -XX:+PrintGCDetails

public class ParallelGC {
    public static void main(String[] args) {
        byte[] b = null;
        for (int i = 0; i < 7; i++)
        {
            b = new byte[6 * 1024 * 1024];
        }
    }
}
[GC (Allocation Failure) [PSYoungGen: 1017K->488K(1536K)] 1017K->624K(19968K), 0.0010570 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1512K->504K(1536K)] 1648K->886K(19968K), 0.0008822 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 701K->504K(1536K)] 13372K->13222K(19968K), 0.0006230 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 504K->504K(1536K)] 13222K->13262K(19968K), 0.0005496 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 12758K->6943K(18432K)] 13262K->6943K(19968K), [Metaspace: 3269K->3269K(1056768K)], 0.0066940 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 27K->80K(1536K)] 13115K->13167K(19968K), 0.0012915 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 80K->96K(1536K)] 13167K->13183K(19968K), 0.0003767 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 96K->0K(1536K)] [ParOldGen: 13087K->6869K(18432K)] 13183K->6869K(19968K), [Metaspace: 3269K->3269K(1056768K)], 0.0067022 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 20K->0K(1536K)] 13034K->13013K(19968K), 0.0003716 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 13013K->13013K(19968K), 0.0002805 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 13013K->6869K(18432K)] 13013K->6869K(19968K), [Metaspace: 3269K->3269K(1056768K)], 0.0064460 secs] [Times: user=0.09 sys=0.02, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 13013K->13013K(19968K), 0.0003008 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 13013K->13013K(19968K), 0.0004655 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 13013K->6869K(18432K)] 13013K->6869K(19968K), [Metaspace: 3269K->3269K(1056768K)], 0.0023795 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 13013K->13013K(19968K), 0.0003551 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 13013K->13013K(19968K), 0.0002971 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 13013K->6869K(18432K)] 13013K->6869K(19968K), [Metaspace: 3269K->3269K(1056768K)], 0.0023131 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 1536K, used 20K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 2% used [0x00000000ffe00000,0x00000000ffe05360,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 18432K, used 13013K [0x00000000fec00000, 0x00000000ffe00000, 0x00000000ffe00000)
  object space 18432K, 70% used [0x00000000fec00000,0x00000000ff8b5680,0x00000000ffe00000)
 Metaspace       used 3275K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 357K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

Serial Old收集器

Serial收集器的老年代版本。基于标记-整理算法实现,

有两个用途:
1、与Serial收集器、Parallel收集器搭配使用
2、作为CMS收集器的后备方案

Parallel Old收集器

Parallel收集器的老年代版本。基于标记-整理算法实现。

CMS收集器

在这里插入图片描述
由于CMS收集器是并发收集器,即在运行阶段用户线程依然在运行,会产生对象,所以CMS收集器不能等到老年代满了才触发,而是要提前触发,这个阈值是92%。这个阈值可以通过参数-XX:CMSInitiatingOccupancyFraction设置

  • 相关参数:
    -XX:+UseConcMarkSweepGC:手动开启CMS收集器
    -XX:+CMSIncrementalMode:设置为增量模式
    -XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
    -XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
    -XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收
    -XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
    -XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

CMS收集器工作分四个步骤:

  • 1、初始标记
    会STW。只标记GC Roots直接关联的对象。
  • 2、并发标记
    不会STW。GC线程与用户线程并发运行。
    会沿着GC Roots直接关联的对象链遍历整个对象图。可想而知需要的时间较长,但因为是与用户线程并发运行的,除了能感知到CPU飙升,不会出现卡顿现象。
  • 3、重新标记
    会STW。
    CMS垃圾收集器通过写屏障+增量更新记录了并发标记阶段新建立的引用关系,重新标记就是去遍历这个记录。
  • 4、并发清除
    GC线程与用户线程并发运行,清理未被标记到的对象
    默认启动的回收线程数 = (处理器核心数 + 3) / 4

显然CMS收集器依然不是完美的,不然后面就不会出现G1、ZGC等。那有哪些缺点呢?
1、运行期间会与用户线程抢夺CPU资源。当然,这是所有并发收集器的缺点
2、无法处理浮动垃圾(标记结束后创建的对象)
3、内存碎片

G1收集器

在这里插入图片描述

  • G1收集器与之前的所有收集器都不一样,它将堆分成了一个一个Region,这些Region用的时候才被赋予角色:Eden、from、to、humongous。一个region只能是一个角色,不存在一个region既是Eden又是from。
  • 每个region的大小可通过参数-XX:G1HeapRegionSize设置,取值范围是2-32M。
  • 一个对象的大小超过region的一半则被认定为大对象,会用N个连续的region来存储。
  • G1名字的由来
    回收某个region的价值大小 = 回收获得的空间大小 + 回收所需时间
    G1收集器会维护一个优先级列表,每个region按价值大小排序存放在这个优先级列表中。收集时优先收集价值更大的region,这就是G1名字的由来。

在这里插入图片描述
四个步骤:

  • 1、初始标记
    会STW。
    做了两件事:
    1、修改TAMS的值,TAMS以上的值为新创建的对象,默认标记为存活对象,即多标
    2、标记GC Roots能直接关联到的对象

  • 2、并发标记
    耗时较长。GC线程与用户线程并发运行。
    从GC roots能直接关联到的对象开始遍历整个对象图

  • 3、最终标记
    遍历写屏障+SATB记录下的旧的引用对象图

  • 4、筛选回收
    更新region的统计数据,对各个region的回收价值进行计算并排序,然后根据用户设置的期望暂停时间的期望值生成回收集。
    然后开始执行清除操作。将旧的region中的存活对象移动到新的Region中,清理这个旧的region。这个阶段需要STW。

  • 相关参数:
    -XX:G1HeapRegionSize:设置region的大小
    -XX:MaxGCPauseMillis:设置GC回收时允许的最大停顿时间(默认200ms)
    -XX:+UseG1GC:开启g1
    -XX:ConcGCThreads:设置并发标记、并发整理的gc线程数
    -XX:ParallelGCThreads:STW期间并行执行的gc线程数

ZGC

ZGC收集器是一款基于Region内存布局的, 暂时不设分代的, 使用了读屏障、 颜色指针等技术来实现可并发的标记-整理算法的, 以低延迟为首要目标的一款垃圾收集器。

ZGC的Region可以具有大、 中、 小三类容量:

  • 小型Region(Small Region) : 容量固定为2MB, 用于放置小于256KB的小对象。
  • 中型Region(Medium Region) : 容量固定为32MB, 用于放置大于等于256KB但小于4MB的对象。
  • 大型Region(Large Region) : 容量不固定, 可以动态变化, 但必须为2MB的整数倍, 用于放置4MB或以上的大对象。

过程:

  • 并发标记(Concurrent Mark):
    与G1一样,并发标记是遍历对象图做可达性分析的阶段,它的初始标记(Mark Start)和最终标记(Mark End)也会出现短暂的停顿,与G1不同的是, ZGC的标记是在指针上而不是在对象上进行的, 标记阶段会更新颜色指针(见下图)Marked 0、 Marked 1标志位

  • 并发预备重分配(Concurrent Prepare for Relocate)
    根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set)。ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本换取省去G1中记忆集的维护成本。

  • 并发重分配(Concurrent Relocate)
    把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障(读屏障(见下面详解))所截获,然后立即根据Region上的转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为称为指针的“自愈”(Self-Healing)能力
    ZGC的颜色指针因为“自愈”(Self-Healing)能力,所以只有第一次访问旧对象会变慢, 一旦重分配集中某个Region的存活对象都复制完毕后, 这个Region就可以立即释放用于新对象的分配,但是转发表还得留着不能释放掉, 因为可能还有访问在使用这个转发表。

ZGC目前有4中机制触发GC:

  • 定时触发,默认为不使用,可通过ZCollectionInterval参数配置。
  • 预热触发,最多三次,在堆内存达到10%、20%、30%时触发,主要时统计GC时间,为其他GC机制使用。
  • 分配速率,基于正态分布统计,计算内存99.9%可能的最大分配速率,以及此速率下内存将要耗尽的时间点,在耗尽之前触发GC(耗尽时间 一次GC最大持续时间 - 一次GC检测周期时间)。
  • 主动触发,(默认开启,可通过ZProactive参数配置) 距上次GC堆内存增长10%,或超过5分钟时,对比距上次GC的间隔时间跟(49* 一次GC的最大持续时间),超过则触发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值