java知识体系整理(二)JVM、GC回收及调优

版权声明:可以转载,需注明出处 https://blog.csdn.net/wthfeng/article/details/88958147

本部分涉及JVM内存结构及GC算法,java调优等知识。

JVM

  1. JVM内存结构划分(堆、栈、方法区、程序计数器等)
  2. 标记清除、标记整理及复制算法。
  3. 常用GC参数
  4. 常见垃圾回收器及优缺点
  5. 程序CPU 100%怎样排查?
  6. 常见的JVM工具有哪些

JVM优化

  1. 栈上分配 当开启逃逸分析后,JVM会把确定不会溢出的对象放到栈上分配,是JVM的一项优化技术,基本思想是对线程私有的对象将他们打散分配到栈上,分配到栈上的对象可以在函数结束时自行销毁,不需要垃圾回收器介入。
  2. TLAB (Thread Local Allocation Buffer) 线程本地分配缓存。为提高分配对象效率,会在Eden区为每个线程单独分配一块空间用于其对象分配。可避免线程分配过程中的同步操作。

注意

TLAB默认是开启的,一般不会很大,且当有大对象时不会再TLAB中分配。

在这里插入图片描述

垃圾收集器

串行收集器(Serial、Serial Old)

Serial串行收集器,单线程收集器,使用复制算法,在单核CPU上可以发挥很好。用于新生代(client默认收集器)。

Serial Old 串行收集器的老年代版本。使用标记清除算法。可以和多种新生代收集器工作。

使用-XX:+UseSerialGC 开启

优点:

  1. 简单高效,没有线程切换开销。
  2. 目前仍是client模式默认收集器。

serial收集器,在GC日志上为
新生代 [DefNew]
老年代 [Tenured]

ParNew(新生代多线程收集器:多线程,独占)

Serial收集器的多线程版本,其参数和Serial一样,使用算法,回收策略也与Serial一样。只是它使用了多线程进行垃圾回收。

可以总结为,除了使用多线程收集以外,与Serial没有什么很大区别,当在单CPU环境下,由于其线程切换的开销,其性能可能还差于Serial收集器。

使用 -XX:+UseParNewGC ParNew收集器用于新生代,会使用串行收集器用于老年代(Serial Old)。

使用 -XX:+UseConcMarkSweepGC ,新生代使用ParNew,老年代使用CMS。

ParallelGC(重吞吐,新生代,多线程)

可以用于控制系统吞吐量,收集算法为复制算法。

使用-XX:MaxGCPauseMills控制收集停顿时间;使用-XX:GCTimeRatio控制吞吐量大小(即用于业务的时间与垃圾回收时间比值,如19,为99%)。

-XX:+UseParallelGC使用ParallelGC收集器,并使用Serial Old作为老年代收集器。

ParallelOldGC(重吞吐,老年代,多线程)

ParallelGC的老年代版本。收集算法为标记-整理算法。

参数-XX:+UseParallelOldGC使用ParallelOldGC收集器,使用ParallelGC作为老年代手机器。

CMS(Concurrent Mark Sweep并发标记清除收集器)

老年代收集器,使用标记-清除算法,关注系统停顿时间。多线程并行收集器。

CMS从整体来说,不像其他收集器垃圾收集时是独占式的,CMS的垃圾收集过程是并发的,和用户线程一起工作。由于CMS工作时没有暂停用户线程,内存可能在收集阶段增长,所以CMS会在内存到达一定阈值后触发回收,而不是等待内存即将满时才回收。使用-XX:CMSInitiatingOccupancyFraction=xx控制回收阈值,xx为百分比。

不过需注意:

  1. 如果CMSInitiatingOccupancyFraction设置过高,当CMS在回收过程中剩余内存不足以容下此段时间新对象的内存,则会发生Concurrent Mode Failure,会停掉整个用户线程,使用Serial Old收集,这样反而会更慢。
  2. CMS使用标记-清除算法,会产生内存碎片。可设置参数-XX:CMSFullGCsBeforeCompaction,表示在进行多少次不压缩Full GC时进行压缩FullGC,压缩收集时,无法并发处理)

CMS开启参数为:-XX:+UseConcMarkSweepGC。线程数默认为:(cpu数+3)/4。

并发收集线程数设置不当,CMS回收垃圾时,可能会导致性能问题。

CMS一共有以下几步:

  1. 初始标记, 标记GC root根节点直接关联对象,很快
  2. 并发标记, 并发标记根节点指向节点,并发(进行可达性分析过程)
  3. 重新标记,修正并发标记期间,因用户线程仍运行导致变动的对象
  4. 并发清除,并发清除对象

CMS目前可与新生代ParNew、Serial搭配。当CMS回收失败时,可用Serial Old进行回收。

G1(分区算法)

待续

收集日志

GC表示停顿类型,Full GC表示发生了STW,如果出现Full GC(System)表示使用了显式GC(调用了System.gc())。

[GC (Allocation Failure) [PSYoungGen: 25600K->4077K(29696K)] 25600K->17349K(98304K), 0.0290039 secs] [Times: user=0.09 sys=0.01, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 23877K->4064K(29696K)] 37149K->33804K(98304K), 0.0423202 secs] [Times: user=0.12 sys=0.01, real=0.04 secs] 
[GC (Allocation Failure) [PSYoungGen: 25708K->4080K(29696K)] 55448K->55246K(98304K), 0.0461796 secs] [Times: user=0.16 sys=0.02, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4080K->0K(29696K)] [ParOldGen: 51166K->50318K(68608K)] 55246K->50318K(98304K), [Metaspace: 3306K->3306K(1056768K)], 0.4442177 secs] [Times: user=0.58 sys=0.01, real=0.45 secs]

方括号数字表示 【GC前该内存区域大小->GC后该内存区域大小(该内存总大小)】

GC (Allocation Failure)表示此次GC是分配失败造成的(内存分配不足)

Full GC 和 Young GC

Full GC指老年代GC,YoungGC为年轻代GC。一般Full GC比较慢,大概比YoungGC慢10倍。

常用GC参数

-Xloggc 保存GC日志

-XX:+PrintGCTimeStamps 打印GC日志时的时间

-XX:+PrintGC 打印GC日志

-XX:+PrintGCDetails 打印详细GC日志

排查策略

cpu消耗过高排查

  1. 使用jps查出运行java进程id(可省略)
  2. 使用top看上一步查到的进程id的cpu消耗(定位进程)
  3. 使用top -Hp pid 命令查询该线程下所有进程情况,查看是否有异常cpu的线程(定位线程)
  4. 使用stack pid(进程id) > stack.log 打印线程堆栈到文件,查看文件。
  5. 将第3部获取到的进程id转为16进制,在线程堆栈里搜索,确定问题线程。
  6. 如果问题线程是自己代码起的线程,查看该代码是否有问题,若是VM Thread(垃圾回收线程),使用jstat -gcutil pid <interval> <count> 命令查看垃圾回收情况,关注YGC(青年代回收次数)和FGC(老年代回收次数)。
  7. 使用jmap -dump:format=b,file=<堆栈名> pid dump堆栈信息,然后用MAT分析堆栈信息。

参考 系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路

猜你喜欢

转载自blog.csdn.net/wthfeng/article/details/88958147
今日推荐