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(操作系统)。
轻量级的线程
纤程的优势:
- 占有的资源少,为什么说他占有资源少呢?举例:操作系统要启一个线程前后要为这个线程配的内存数据大概有1M,而纤程大概是4K
- 由于纤程非常的轻量级,所以切换比较简单
- 可以同时被启动很多个(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日志分析纤程的主要内容,如果未能解决你的问题,请参考以下文章