首页 > 系统相关 >jvm垃圾回收及内存分配

jvm垃圾回收及内存分配

时间:2024-05-14 16:09:37浏览次数:21  
标签:标记 0.0 回收 GC 内存 jvm CMS 垃圾

目录

0.垃圾收集器的相关JVM参数

-XX:+UseSerialGC // 指定新生代和老年代使用串行化垃圾收集器
-XX:+UseParNewGC // 指定新生代使用ParNew收集器,老年代使用串行化收集器
-XX:ParallelGCThreads=5 // 指定ParNew回收器工作时的线程数量,最好跟CPU数量相当,默认为CPU数量,当 CPU 数量大于8个时,数量为 3+((5*CPU个数)/8)
—XX:+UseParallelGC // 指定新生代使用ParallelGC收集器,老年代使用串行化收集器
-XX:+UseParallelOldGC // 指定新生代使用ParallelGC,老年代使用ParallelOldGC收集器
-XX:MaxGCPauseMillis=X // 指定最大STW时间,值大于0,ParallelGC 在工作时,会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMillis 以内。如果希望减少停顿时间,而把这个值设得很小,为了达到预期的停顿时间,虚拟机可能会使用一个较小的堆(一个小堆比一个大堆回收快),而这将导致垃圾回收变得很频繁,从而增加了垃圾回收总时间,降低了吞吐量。    
-XX:GCTimeRatio=X // 设置系统吞吐量大小,值为0-100范围,如果GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的时间用于垃圾收集。比如GCTimeRatio 等于 19(默认值),则系统用于垃圾收集的时间不超过 1/(1+19)=5%。默认情况下,它的取值是 99,即不超过 1/(1+99)=1%的时间用于垃圾收集。
-XX:+UseAdaptiveSizePolicy // 开启GC自适应策略。新生代,eden,survivor等相关信息都会被调整
-XX:+UseConcMarkSweepGC // 新生代使用 ParNew 回收器,老年代使用 CMS,默认线程数量是 (ParallelGCThreads + 3)/4
-XX:-CMSPrecleaningEnabled // CMS中,不进行预清理操作
-XX:ConcGCThreads // 执行并发线程数量,也可以用于控制CMS收集器
-XX:ParallelCMSThreads // 指定CMS并发线程数量
-XX:CMSInitiatingOccupancyFraction=X // 执行CMS回收阈值,默认是68 当老年代使用率达到68%时,会进行CMS回收
-XX:+UseCMSCompactAtFullCollection:// 指定在CMS回收完成后,是否开启内存碎片整理
-XX:CMSFullGCsBeforeCompaction=X // 指定进行多少次CMS回收后,进行内存随便整理
-XX:+CMSClassUnloadingEnabled // 开启CMS回收永久区/MetaSpace
-XX:+UseG1GC // 使用G1垃圾收集器 

1.java垃圾回收器种类

  • 串行垃圾回收器
  • 并行垃圾回收器
  • CMS回收器
  • G1回收器
  • ...

2.串行回收器

  • 串行回收器是指使用单线程进行垃圾回收的回收器。每次回收时,串行回收器只有一个工作线程
  • 串行回收器可以在新生代和老年代使用,根据作用于不同的堆空间,分为新生代串行回收器和老年代串行回收器。

串行垃圾收集器的特点:

第一,它仅仅使用单线程进行垃圾回收

第二 ,它是独占式的垃圾回收

在串行收集器进行垃圾回收时 Java 应用程序中的线程都需要暂停 ,等待垃圾回收的完成

新生代使用复制算法,老年代使用标记压缩算法,因为老年代存活对象比较多,因此每次老年代GC时时间会比较长,STW也会比较长

image

SerialGC输出格式

[GC (Allocation Failure) [DefNew: 960K->63K(960K), 0.0017692 secs] 1428K->778K(1048512K), 0.0018002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 1047551K->1047551K(1047552K), 0.7139298 secs] 1048511K->1048508K(1048512K), [Metaspace: 3795K->3795K(1056768K)], 0.7139578 secs] [Times: user=0.70 sys=0.00, real=0.71 secs] 

ParNewGC输出格式

[GC (Allocation Failure) [ParNew: 959K->64K(960K), 0.0008930 secs] 37175K->37255K(1048512K), 0.0009207 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 1047551K->1047551K(1047552K), 0.6854967 secs] 1048511K->1048480K(1048512K), [Metaspace: 3817K->3817K(1056768K)], 0.6855433 secs] [Times: user=0.67 sys=0.02, real=0.68 secs] 

ParallelGC输出格式

[GC (Allocation Failure) [PSYoungGen: 992K->512K(1024K)] 40972K->40844K(1048064K), 0.0005923 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 512K->0K(1024K)] [ParOldGen: 1044940K->944464K(1047040K)] 1045452K->944464K(1048064K), [Metaspace: 3788K->3788K(1056768K)], 0.6676265 secs] [Times: user=4.74 sys=0.03, real=0.67 secs] 

CMS GC输出格式

[GC (CMS Initial Mark) [1 CMS-initial-mark: 1047551K(1047552K)] 1048511K(1048512K), 0.0002479 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [CMS[CMS-concurrent-mark: 0.266/0.267 secs] [Times: user=0.70 sys=0.02, real=0.27 secs] 
 (concurrent mode failure): 1047551K->1047551K(1047552K), 0.9103748 secs] 1048511K->1048507K(1048512K), [Metaspace: 3789K->3789K(1056768K)], 0.9104161 secs] [Times: user=1.36 sys=0.02, real=0.91 secs] 

G1 GC输出格式

[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0021869 secs]
   [Parallel Time: 1.3 ms, GC Workers: 10]
      [GC Worker Start (ms): Min: 2134.1, Avg: 2134.1, Max: 2134.2, Diff: 0.1]
      [Ext Root Scanning (ms): Min: 0.2, Avg: 0.3, Max: 0.4, Diff: 0.2, Sum: 2.9]
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Object Copy (ms): Min: 0.6, Avg: 0.7, Max: 0.7, Diff: 0.1, Sum: 6.5]
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Termination Attempts: Min: 1, Avg: 8.5, Max: 13, Diff: 12, Sum: 85]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
      [GC Worker Total (ms): Min: 0.9, Avg: 1.0, Max: 1.0, Diff: 0.1, Sum: 9.9]
      [GC Worker End (ms): Min: 2135.1, Avg: 2135.1, Max: 2135.1, Diff: 0.0]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.1 ms]
   [Other: 0.8 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.2 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.1 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 3072.0K(10.0M)->0.0B(8192.0K) Survivors: 0.0B->2048.0K Heap: 8192.0K(20.0M)->6304.0K(20.0M)]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 

3. 并行回收器

  • 并行回收器在串行回收器的基础上做了改进,它使用多个线程同时进行垃圾回收。对于并行能力强的计算机,可以有效缩短垃圾回收所需的实际时间。

3.1 ParNew回收器

  • ParNew 回收器是一个工作在新生代的垃圾收集器。它将串行回收器多线程化,回收策略、算法以及参数和新生代串行回收器一样。
  • ParNew 回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。

image

3.2 ParallelGC 回收器

  • ParallelGC 回收器也是使用复制算法的收集器。
  • 和ParNew 回收器一样,都是多线程、独占式的收集器。但ParallelGC 回收器有非常关注系统的吞吐量。
  • ParallelGC支持一种自适应的GC 调节策略。使用-XX:+UseAdaptiveSizePolicy 可以打开自适应 GC 策略。在这种模式下,新生代的大小、eden 和 survivior 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。在手工调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆、目标吞吐量(GCTimeRatio)和停顿时间(MaxGCPauseMillis),让虚拟机自己完成调优工作。

3.3 ParallelOldGC 回收器

  • 老年代 ParallelOldGC 回收器也是一种多线程并发的收集器。

  • 和新生代 ParallelGC 回收器样,它也是一种关注吞吐量的收集器。

  • 和ParallelGC 新生代回收器搭配使用。

  • ParallelOldGC 回收器使用标记压缩算法

image

3.4 CMS 回收器

  • 与ParallelGC 和ParallelOldGC 不同,CMS 回收器主要关注于系统停顿时间。
  • CMS是Concurrent Mark Sweep的缩写,意为并发标记清除,
  • 它使用的是标记清除算法,同时它又是一个使用多线程并行回收的垃圾回收器。

CMS 主要工作步骤

CMS工作时,主要步骤有:

1. 初始标记
2. 并发标记
3. 预清理
4. 重新标记
5. 并发清除和并发重置

其中初始标记和重新标记是独占系统资源的,而预清理、并发标记、并发清除和并发重置是可以和用户线程一起执行的。CSM 收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

image

CMS说明

  1. 初始标记、并发标记和重新标记都是为了标记出需要回收的对象。

  2. 并发清理则是在标记完成后,正式回收垃圾对象。

  3. 并发重置是指在垃圾回收完成后,重新初始化CMS数据结构和数据,为下一次垃圾回收做好准备。

  4. 并发标记、并发清理和并发重置都是可以和应用程序线程一起执行的。

  • 预清理是并发的,除了为正式清理做准备和检查以外,预清理还会尝试控制一次停顿时间。

  • 由于重新标记是独占CPU的,如果新生代 GC 发生后,立即触发一次重新标记,那么一次停顿时间可能很长。预清理为了避免这种情况,预处理时,会刻意等待一次新生代GC的发生,然后根据历史性能数据预测下一次新生代GC可能发生的时间,然后在当前时间和预测时间的中间时刻,进行重新标记。这样,从最大程度上避免新生代 GC和重新标记重合,尽可能减少一次停顿时间。

  • CMS回收器不是独占式的回收器

  • 在CMS回收过程中,应用程序仍然在不停地工作,会不断地产生垃圾。这些新生成的垃圾在当前CMS回收过程中是无法清除的。所以在CMS回收过程中,还应该确保应用程序有足够的内存可用。因此,CMS回收器不会等待堆内存饱和时才进行垃圾回收,而是当堆内存使用率达到某一阈值时便开始进行回收,以确保应用程序在CMS工作过程中,依然有足够的空间支持应用程序运行。

  • 回收阈值可以使用-XX:CMSInitiatingOccupancyFraction来指定,默认是68。即当老年代的空间使用率达到68%时,会执行一次CMS回收。如果应用程序的内存使用率增长很快,在CMS的执行过程中,已经出现了内存不足的情况,此时,CMS回收就会失败,虚拟机将启动老年代串行收集器进行垃圾回收。如果这样,应用程序将完全中断,直到垃圾回收完成,这时,应用程序的停顿时间可能会较长。

3.5 G1回收器

  • G1回收器(Garbage-First)是在JDK 1.7中正式使用的全新的垃圾回收器
  • 目的是为了取代 CMS 回收器。
  • G1回收器拥有独特的垃圾回收策略。
    • 从分代上看,G1依然属于分代垃圾回收器,它会区分年轻代和老年代,依然有eden区和 survivor 区,
    • 从堆的结构上看,它并不要求整个 eden 区、年轻代或者老年代都连续。
    • 使用了分区算法。
  1. 并行性: G1在回收期间,可以由多个GC线程同时工作,有效利用多核计算能力。
  2. 并发性: G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此一般来说,不会在整个回收期间完全阻塞应用程序。
  3. 分代GC: G1依然是一个分代收集器,但是和之前回收器不同,它同时兼顾年轻代和老年代。对比其他回收器,它们或者工作在年轻代,或者工作在老年代。因此,这里是一个很大的不同。
  4. 空间整理: G1在回收过程中,会进行适当的对象移动,不像CMS,只是简单地标记清理对象,在若于次 GC后,CMS必须进行一次碎片整理。而G1不同,它每次回收都会有效地复制对象,减少空间碎片。
  5. 可预见性: 由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿也能得到较好的控制。

G1垃圾收集器的4个阶段

  • 新生代GC: 回收eden和survivor区
  • 并发标记周期:
  • 混合收集
  • 如果需要,可能会进行 FullGC

3.5.1 新生代GC

新生代 GC 的主要工作:

  1. 回收 eden 区和 survivor 区。

  2. 一旦 eden 区被占满,新生代 GC 就会启动。

  3. 新生代 GC 只处理 eden 和 survivor 区,回收后,所有的 eden 区都应该被清空,而 survivor 区会被收集一部分数据,但是应该至少仍然存在一个 survivor 区

  4. 老年代的区域增多,因为部分 survivor 区或者eden 区的对象可能会晋升到老年代。

image

3.5.2 G1的并发标记周期

  1. 初始标记: 标记从根节点直接可达的对象。这个阶段会伴随一次新生代GC,它是会产生全局停顿的,应用程序线程在这个阶段必须停止执行。
  2. 根区域扫描: 由于初始标记必然会伴随一次新生代GC,所以在初始化标记后,eden 被清空,并且存活对象被移入survivor 区。在这个阶段,将扫描由 survivor 区直接可达的老年代区域,并标记这些直接可达的对象。这个过程是可以和应用程序并发执行的。但是根区域扫描不能和新生代 GC 同时执行(因为根区域扫描依赖survivor 区的对象,而新生代 GC 会修改这个区域),因此如果恰巧在此时需要进行新生代GC,GC 就需要等待根区域扫描结束后才能进行,如果发生这种情况,这次新生代GC的时间就会延长
  3. 并发标记: 和CMS类似,并发标记将会扫描并查找整个堆的存活对象,并做好标记。这是一个并发的过程,并且这个过程可以被一次新生代 GC 打断。
  4. 重新标记: 和CMS一样,重新标记也是会产生应用程序停顿的。由于在并发标记过程中,应用程序依然在运行,因此标记结果可能需要进行修正,所以在此对上一次的标记结果进行补充。在G1中,这个过程使用SATB(Snapshot-At-The-Beginning)算法完成即 G1会在标记之初为存活对象创建一个快照,这个快照有助于加速重新标记的速度。
  5. 独占清理:这个阶段是会引起停顿的。它将计算各个区域的存活对象和GC回收比例并进行排序,识别可供混合回收的区域。在这个阶段,还会更新记忆集(RemeberedSet)。该阶段给出了需要被混合回收的区域并进行了标记,在混合回收阶段,需要这些信息。
  6. 并发清理阶段: 这里会识别并清理完全空闲的区域。它是并发的清理,不会引起停顿。

image

image

  1. 初始化标记,伴随一次新生代GC,eden被清空,存活对象复制到survivor

image

  1. 根区域并发扫描,不能被新生代GC打断,期间会产生STW

image

  1. 并发标记,可以被新生代GC 打断,下面的日志显示了一次并发标记被3 次新生代 GC 打断。

image

  1. 重新标记,会引起全局STW

image

  1. 独占清理,会重新计算各个区域的存活对象,并以此可以得到每个区域进行GC的回收比。

image

  1. 并发清理,是并发执行的,会根据独占清理阶段计算得出的每个区域的存活对象数量,直接回收已经不包含存活对象的区域。

image

3.5.3 混合回收

  • 并发标记周期中,回收垃圾的比例是相当低的。

  • 并发标记周期后,G1已经明确知道哪些区域含有比较多的垃圾对象,在混合回收阶段,就可以专门针对这些区域进行回收。

  • G1会优先回收垃圾比例较高的区域,因为回收这些区域的性价比也比较高。而这也正是G1名字的由来。G1全称为Garbage First Garbage Collector,直译为垃圾优先的垃圾回收器,这里的垃圾优先(Garbage First)指的就是回收时优先选取垃圾比例最高的区域。

    混合回收,在这个阶段,既会执行正常的年轻代GC,又会选取一些被标记的老年代区域进行回收,它同时处理了新生代和老年代。

image

  • 因为新生代GC的原因,eden 区域必然被清空,此外,有两块被标记位G的垃圾比例最高的区域被清理。被清理区域中的存活对象会被移动到其他区域,这样的好处是可以减少空间碎片。

混合GC日志

image
.png)

3.5.4 G1日志解析

  1. 日志第一行

image

表示在应用程序开启1.619秒时发生了一次新生代GC,这是在初始标记时发生的,耗时0.038 秒,意味着应用程序至少暂停了0.038秒。

  1. 后续并行时间

image

表示所有 GC 线程总的花费时间,这里为38毫秒

  1. 每一个 GC 线程的执行情况

image

GC 线程的执行情况,一共4个GC线程,它们都在 1619.3 秒时启动。同时给出了这几个启动数据的统计值,如平均(Avg),最小(Min)、最大(Max)和差值(Diff),Diff 表示最大值和最小值的差。

  1. 根扫描的耗时

image

在根扫描时(全局变量、系统数据字典、线程栈等),每一个GC线程的耗时,这里分配消耗了 0.3、0.3、0.2、0.2秒时间,后一行给出这些耗时的统计数据。

  1. 更新记忆集(Remember Set)的耗时

image
.png)

记忆集是 G1中维护的一个数据结构,简称 RS。每一个 G1 区域都有一个 RS 与之关联。由于 G1 回收时,是按照区域回收的,比如在回收区域 A的对象时,很可能并不回收区域B的对象此时,为了回收区域 A 的对象,要扫描区域 B 甚至是整个堆来判定区域 A 中哪些对象不可达,这样做的代价显然很大。因此,G1在区域A的RS中,记录了在区域A中被其他区域引用的对象,这样在回收区域A 时,只要将 RS 视为区域A 根集的一部分即可,从而可以避免做整个堆的扫描。由于系统在运行过程中,对象之间的引用关系是可能时刻变化的,因此为了更高效地跟踪这些引用关系,会将这些变化记录在 Update Bufers 中。这里的 Processed Buffers 指的就是处理这个 Update Buffers数据。这里给出的4个时间和也是4个GC 线程的耗时,以及它们的统计数据。从这个日志中可以看到,更新 RS 时,分别耗时 5.7、5.4、28、5.3 毫秒,平均耗时 11.1 毫秒。

  1. 扫描 RS 的时间

image

  1. 在正式回收时,G1会对被回收区域的对象进行疏散,即将存活对象放置在其他区域中,因此需要进行对象的复制。

标签:标记,0.0,回收,GC,内存,jvm,CMS,垃圾
From: https://www.cnblogs.com/ccblblog/p/18191516

相关文章

  • idea jprofiler内存快照分析
    1、idea按照jprofiler插件setting->plugins->marketplace搜索jprofiler,安装并重启2、分析dump文件dump内存快照方式:jmap-dump:format=b,file=heapdump.phrof{jvm的pid}使用jprofiler导入dump文件导入成功3、jprofiler工具关注“BiggestObjects”、"GCroots"、"Packa......
  • 内存溢出问题分析与处理
    内存占用情况查询检查系统内存使用情况以GiB为单位进行显示remotecmd"free-g"1.total:物理内存总量(GiB)。2.used:当前已使用的物理内存量(不包括缓冲区和缓存)(GiB)。3.free:可用的物理内存量(GiB)。4.shared:被多个进程共享的内存量(GiB)。5.buff/cache:作为......
  • 内存
    内存内存的基本概念内存是用来存储数据的社保,它的存储熟读介于寄存器和硬盘之间内存是CPU唯一可以访问的打容量的存储设备!所有硬盘中的程序和数据必须调入内存之后方可被CPU执行!切记:CPU不能直接处理硬盘中的数据内存的问题是软件开发中最核心的问题之一!如内存的分配,内存的释......
  • Linux上执行内存中的脚本和程序
    在Linux中可以不需要有脚本或者二进制程序的文件在文件系统上实际存在,只需要有对应的数据在内存中,就有办法执行这些脚本和程序。原理其实很简单,Linux里有办法把某块内存映射成文件描述符,对于每一个文件描述符,Linux会在/proc/self/fd/<文件描述符>这个路径上创建一个对应描述符的......
  • 垃圾回收机制
    垃圾回收机制【一】什么是垃圾回收机制垃圾回收机制(GC机制)是python自带的机制专门用来回收变量值所占的内存空间【二】在python中的垃圾#每次书写Python代码都会创建很多变量名和变量值#但是有很多变量名和变量值用过一次就不用了#--->被称之为垃圾--->不会主......
  • golang进程通过共享内存和C++进程进行通信
    目录serverclientserverC++可以使用POSIX共享内存API来创建和管理共享内存server.cpp#include<fcntl.h>#include<sys/mman.h>#include<sys/stat.h>#include<unistd.h>#include<cstring>#include<iostream>constchar*S......
  • 【java】问题排查-内存溢出(OOM)-汇总指南
    1、java.lang.OutOfMemoryError:Javaheapspace原因分析示例解决方案2、java.lang.OutOfMemoryError:GCoverheadlimitexceeded原因分析示例解决方案3、java.lang.OutOfMemoryError:Permgenspace原因分析示例解决方案4、java.lang.OutOfMemoryErr......
  • 【java】内存溢出(OOM)
    一、OOM简介 1、什么OOM?(图片来源网络,侵删)OOM,全称OutOfMemory,意思是内存耗尽或内存溢出。对应Java程序抛出的错为java.lang.OutOfMemoryError,OutOfMemoryError(OOM)是Java虚拟机(JVM)抛出的一个异常,表示JVM没有足够的内存来完成请求的内存操作。当JVM尝试为新的对象分配......
  • JVM自定义加类加载器
    在JVM类加载器分类中提及JVM自带的加载器无法满足实际业务需求时,可以自定义加载器。那一般什么情况下需要自定义加载器呢?隔离加载类:模块隔离——把类加载到不同的应用选项中,比如Tomcat类加载器。修改类加载方式:平台提供了三类加载器除必须加载的类加载器,可以根据实际情况......
  • openGauss 内存不足问题
    内存不足问题问题现象客户端或日志里出现错误:memoryusagereachthemax_dynamic_memory。原因分析出现内存不足可能因GUC参数max_process_memory值设置较小相关,该参数限制一个openGauss实例可用最大内存。处理分析通过工具gs_guc适当调整max_process_memory参数值。注意需......