JVM调优--07---GC日志分析纤程

Posted 高高for 循环

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM调优--07---GC日志分析纤程相关的知识,希望对你有一定的参考价值。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


GC 日志分析

通过阅读 GC 日志,我们可以了解 Java 虚拟机内存分配与回收策略。 内存分配与垃圾回收的参数列表

-XX:+PrintGC 输出 GC 日志。类似:-verbose:gc
-XX:+PrintGCDetails 输出 GC 的详细日志
-XX:+PrintGCTimestamps 输出 GC 的时间戳(以基准时间的形式)
-XX:+PrintGCDatestamps 输出 GC 的时间戳(以日期的形式,如2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC在进行 GC 的前后打印出堆的信息
-Xloggc:…/logs/gc.log日志文件的输出路径



把 GC 日志保存到文件

-Xloggc:/path/to/gc.log







GC 回收举例

我们编写一个程序,用来说明 GC 收集的过程

/**
 * GC垃圾收集过程
 */
public class GCUseTest 
    static final Integer _1MB = 1024 * 1024;
    public static void main(String[] args) 
        byte [] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 *_1MB];
        allocation2 = new byte[2 *_1MB];
        allocation3 = new byte[2 *_1MB];
        allocation4 = new byte[4 *_1MB];
    

我们设置 JVM 启动参数

-Xms10m -Xmx10m -XX:+PrintGCDetails

  • 首先我们会将3个 2M 的数组存放到 Eden 区,然后后面 4M 的数组来了后,将无法存储,因为 Eden 区只剩下2M的剩余空间了,那么将会进行一次 Young GC 操作,将原来 Eden 区的内容,存放到 Survivor 区,但是Survivor 区也存放不下,那么就会直接晋级存入 Old 区

    然后我们将 4M 对象存入到 Eden 区中

GC 日志工具

可以用一些工具去分析这些 GC 日志

常用的日志分析工具有:GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat 等

GCViewer

GCEasy

https://gceasy.io/

CMS日志分析

回收过程:


CMS的问题

  • 浮动垃圾
  • 碎片化

日志分析

执行命令:

java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01

  • ParNew:年轻代收集器
  • 6144->640:收集前后的对比
  • (6144):整个年轻代容量
  • 6585 -> 2770:整个堆的情况
  • (19840):整个堆大小
[GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
	//8511 (13696) : 老年代使用(最大)
	//9866 (19840) : 整个堆使用(最大)
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
	//这里的时间意义不大,因为是并发执行
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
	//标记Card为Dirty,也称为Card Marking
[GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
	//STW阶段,YG occupancy:年轻代占用及容量
	//[Rescan (parallel):STW下的存活对象标记
	//weak refs processing: 弱引用处理
	//class unloading: 卸载用不到的class
	//scrub symbol(string) table: 
		//cleaning up symbol and string tables which hold class-level metadata and 
		//internalized string respectively
	//CMS-remark: 8511K(13696K): 阶段过后的老年代占用及容量
	//10108K(19840K): 阶段过后的堆占用及容量

[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
	//标记已经完成,进行并发清理
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
	//重置内部结构,为下次GC做准备

G1日志详解

回收过程

日志详解

[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
//young -> 年轻代 Evacuation-> 复制存活对象 
//initial-mark 混合回收的阶段,这里是YGC混合老年代回收
   [Parallel Time: 1.5 ms, GC Workers: 1] //一个GC线程
      [GC Worker Start (ms):  92635.7]
      [Ext Root Scanning (ms):  1.1]
      [Update RS (ms):  0.0]
         [Processed Buffers:  1]
      [Scan RS (ms):  0.0]
      [Code Root Scanning (ms):  0.0]
      [Object Copy (ms):  0.1]
      [Termination (ms):  0.0]
         [Termination Attempts:  1]
      [GC Worker Other (ms):  0.0]
      [GC Worker Total (ms):  1.2]
      [GC Worker End (ms):  92636.9]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.0 ms]
   [Other: 0.1 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.0 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.0 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
//以下是混合回收其他阶段
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000078 secs]
[GC concurrent-mark-start]
//无法evacuation,进行FGC
[Full GC (Allocation Failure)  18M->18M(20M), 0.0719656 secs]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]

纤程

进程、线程、纤程

用户态和内核态:

纤程概念:

  • 进程:是操作系统资源分配的基本单位,比如内存、打开文件、网络IO,分配了独立的内存空间
  • 线程:是操作系统资源调度的基本单位,cpu分配的基本单位

纤程:是用户态的线程,是线程中的线程,切换和调度不需要经过OS(操作系统)

轻量级的线程

纤程的优势:

  1. 占有的资源少,为什么说他占有资源少呢?举例:操作系统要启一个线程前后要为这个线程配的内存数据大概有1M,而纤程大概是4K
  2. 由于纤程非常的轻量级,所以切换比较简单
  3. 可以同时被启动很多个(10万个都没问题)

市场应用

  • 目前支持内置纤程的语言Kotlin Scala Go Python等,
  • 可惜的是,Java没有官方的纤程支持,好在有个叫做Quasar的库可堪一用

Quasar库

pom.xml

<!-- https://mvnrepository.com/artifact/co.paralleluniverse/quasar-core -->
<dependency>
    <groupId>co.paralleluniverse</groupId>
    <artifactId>quasar-core</artifactId>
    <version>0.7.6</version>
</dependency>

java 纤程 案例

 
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.SuspendableRunnable;
 
import java.util.concurrent.ExecutionException;
 
 
// 纤程:是用户态的线程,是线程中的线程,切换和调度不需要经过OS(操作系统)。;轻量级的线程 - 线程
public class T02_HelloFiberV2 
    public static void main(String[] args) throws InterruptedException, ExecutionException 
        long start = System.currentTimeMillis();
        int size = 10000;
        Fiber<Void>[] fibers = new Fiber[size];
        for (int i = 0; i < fibers.length; i++) 
            fibers[i] = new Fiber<Void>(new SuspendableRunnable() 
 
                @Override
                public void run() throws SuspendExecution, InterruptedException 
                    calc();
                
            );
        
 
        for (int i = 0; i < fibers.length; i++) 
            fibers[i].start();
        
 
        for (int i = 0; i < fibers.length; i++) 
            fibers[i].join();
        
 
        long end = System.currentTimeMillis();
        System.out.println(end - start);
 
    
 
    private static void calc() 
        int result = 0;
        for (int m = 0; m < 10000; m++) 
            for (int i = 0; i < 200; i++) 
                result += i;
            
        
    

多线程与多纤程计算耗时对比结果如下:


纤程的应用场景:

纤程 vs 线程池:很短的计算任务,不需要和内核打交道,并发量高!

以上是关于JVM调优--07---GC日志分析纤程的主要内容,如果未能解决你的问题,请参考以下文章

jvm调优-从eclipse开始

JVM:JVM 调优 - 从 Eclipse 开始

JVM调优日志解析分析与性能监控工具

jvm性能调优实战 - 61常用的JVM调优网站

JVM调优——之CMS GC日志分析

JVM性能调优1:JVM性能调优理论及实践(收集整理)