垃圾回收及JVM调优

GC相关

Posted by Steven on 2021-05-23
Estimated Reading Time 42 Minutes
Words 9.5k In Total
Viewed Times

何为Garbage?

没有任何引用的对象就是垃圾

找到垃圾的方式

1、引用计数 不能解决循环引用(一团互相引用,但是这一团整体没有引用,是一团垃圾)

2、根可达算法

线程栈变量、静态变量、常亮池、JNI指针(本地方法栈)

image-20210523174650471

垃圾回收算法(GC算法)

  • Mark-Sweep(标记清除)
    算法相对简单,适合存活对象比较多(需要清理的比较少)的时候效率较高
    劣势:需要两遍扫描(第一遍找到存活的标记,第二遍找到没用的,清除),执行效率偏低,容易产生碎片

  • Copying(拷贝)
    内存一分为二,把有用的拷贝到下面,剩下的上面的全部清除掉

    适用于存活对象较少的,只扫描一次,效率提高,没有碎片
    劣势:空间浪费,会发生移动复制对象,需要调整对象引用

    image-20210523181345569

  • Mark-Compact(标记压缩)
    “整理到房间的一个角落中“,剩下的空间就清理出来了
    image-20210523181953834

JVM内存分代模型(用于分代垃圾回收算法)

是部分垃圾回收器使用的模型

除了Epsilicon(用来做debug的垃圾回收器)、ZGC、Shenndoah之外的GC都是使用逻辑分带模型
G1是逻辑分区,物理不分带

除此之外不仅逻辑分带,而且物理分带

新生代+老年代+永久带(1.7)/元数据区(1.8)

新生代,老年代这是堆空间,MethodArea 这是堆之外的空间

MethodArea(方法区) 逻辑概念,以前1.7 的时候叫 永久带,1.8叫 元数据区,存的是c la ss的元信息,代码的编译信息,JIT编译好的代码信息等等

1.7的时候这个空间需要限制大小,但是因为有了限制,所以容易溢出

1.8之后受限于物理内存,

字符串常亮,1,7在MethodArea中,1.8在堆内存中

堆内存逻辑分区

image-20210524001006075

image-20210523184247507

一个对象产生,尝试先往栈上仍,栈上扔不下,往(伊甸)Eden区仍,经过一次垃圾回收机制,就到了s1区(幸存区域或者叫from),再经过一次垃圾回收之后,进入s2(to),经过多次垃圾回收机制,进入到old区

image-20210523185042384

上图凡是在年轻带进行回收的,叫MinorGC或者YGC,在年轻带空间耗尽时触发

凡事在老年代进行回收的,叫MajorGC或者FullGC,在老年代无法继续分配空间时触发,新生代老年代同时进行回收

image-20210523232152451

image-20210523233350850

对象如何进入老年代

new 出来一个对象,首先尝试在栈上分配,如果栈上能分配,就直接分配到栈上(栈上有个好处,就是直接栈往出一弹pop,不需要垃圾回收)

如果说分配不下,看这个对象大不大,这个大不大是有一个参数指定的,如果大的话,直接进入老年代,需要FGC才能结束

如果不大的话,会进入TLAB,不管是线程本地分配 ,还是直接到伊甸区,总而言之都是到伊甸区

伊甸区经过GC清除,如果清除完了,直接结束

如果没有清完,进入S1,S1再进行GC清除,如果年龄够了,进old区,如果年龄不够,进s2(这中间有动态年龄判断)

S2重复上面的过程,GC清除,然后要么进s1,要么进入old区

image-20210523234039422

-Xms起始堆内存大小

-Xmx 最大的堆内存大小

java -XX:+PrintFlagsFinal -version

jvm误区–动态对象年龄判定

动态对象年龄判断,主要是被TargetSurvivorRatio这个参数来控制。而且算的是年龄从小到大的累加和,而不是某个年龄段对象的大小。看完后先记住这个参数吧TargetSurvivorRatio,虽然你以后基本不会调整他。

https://www.jianshu.com/p/989d3b06a49d

分配担保:

YGC

期间,survivor区空格键不够了,空间担保直接进入老年代

常见的垃圾回收器

image-20210524012822842

常见的组合

image-20210524013039900

并且这三对不仅在逻辑上分年轻代和老年代,而且在物理上也分

Serial 单线程

Parallel 多线程

Serial

当进行垃圾回收的时候,所有的工作线程全部停止

STW(stop-the-world)

safe point(找一个安全的点停止线程)

image-20210524014110935

停顿时间较长,因此用的比较少

Serial Old

用的是标记清除或者标记压缩算法,不过也是单线程

image-20210524014409748


parallel Scavenge(PS) PS+PO

现在的jvm,如果在上线前没有做任何的调优,那么默认就是parallel Scavenge

跟前面的区别就是,清理线程是多个的

image-20210524014825762

parallel old(PO)

image-20210524014903195


ParNew+CMS

ParNew(PN)

其实就是 parallel new

只是对PS 做了一些增强,以便和CMS配合使用

目前还没有不STW的垃圾回收器

CMS

即concurrent mark sweep(concurrent:并发)

JDK诞生Serial追随 为了提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入的,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病很多,因此目前没有任何一个JDK版本默认是CMS的

从线程角度理解

image-20210524020146862

就是与其他工作一起

Parallel叫并行,是指多个线程同时回收,并发指的是我在进行垃圾回收的时候,你还可以产生新的垃圾

并发垃圾回收的出现是因为无法忍受STW

CMS的几个阶段

初始标记 :直接标记最根上的对象

image-20210524104307225

并发标记 :这块是最浪费时间的(80% 的时间),同应用程序一起运行

重新标记 :上一步中多数垃圾在并发标记中已经标记完了,因此到这一步之后,基本都是在并发标记过程中,应用程序继续运行产生的新的垃圾,这部分垃圾很少,所以使用STW,最后进行一个并发清理

并发清理 :并发清理的时候也会产生新的垃圾,这种垃圾叫浮动垃圾,这种垃圾就需要下一次CMS运行清理

CMS的两大问题

1、内存碎片化

2、浮动垃圾

当老年代的浮动垃圾满了之后,并且空间中还有浮动垃圾的时候,就需要请出老奶奶“Serial Old”拿着苕帚(单线程)在那扫垃圾

CMS本来是为了解决停顿时间较长的问题, 因此一旦出现上面说的问题,那么垃圾回收的时间就会非常长

疑问? 上面的情况为啥不用PO多线程来清理垃圾,而是使用Serial Old

解决方案:降低CMS触发的阈值

有一个配置-XX:CMSInitiatingOccupancyFraction, 通过降低这个值(这个值默认是92%:是指达到92%的时候就会产生FGC),给浮动垃圾流出部分空间,即进行回收的时候,还留出空间让浮动垃圾进来

CMS面对很大块内存的时候,会产生很长时间的卡顿

例子:

1、硬件升级系统反而卡顿的问题

concurrent mark 阶段的算法(并发标记)

CMS使用的是三色标记+Incremental Update

G1用的是三色标记+SATB

ZGC用的是颜色指针+写屏障

Shenandoah 用的是颜色指针+读屏障

垃圾收集器跟内存大小的关系

1、Serial 几十兆

2、PS 上百兆~几个G

3、CMS 20G

4、G1 上百G

5、ZGC 4T

常见垃圾回收器组合参数设定:(1.8)

  • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
    • 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
  • -XX:+UseParNewGC = ParNew + SerialOld(如果没有明确指定Old区的垃圾回收器,默认是SerialOld)
  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
  • -XX:+UseG1GC = G1
  • Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC
    • java +XX:+PrintCommandLineFlags -version
    • 通过GC的日志来分辨
  • Linux下1.8版本默认的垃圾回收器到底是什么?
    • 1.8.0_181 默认(看不出来)Copy MarkCompact
    • 1.8.0_222 默认 PS + PO

JVM调优第一步,JVM常用命令

JVM的命令行参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

HotSpot参数分类

标准: - 开头,所有的HotSpot都支持

非标准:-X 开头,特定版本HotSpot支持特定命令

不稳定:-XX 开头,下个版本可能取消

java -version

java -X

区分两个概念

区分概念:内存泄漏memory leak,内存溢出out of memory

内存泄漏 :一整块内存中有其中一小块内存分配了空间之后,其他人再也用不了这块内存,被一个废了的对象占用这,也没有回收它。泄漏不一定产生溢出

内存溢出:不断地产生对象,内存满了

  1. java -XX:+PrintCommandLineFlags HelloGC
  2. java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC PrintGCDetails PrintGCTimeStamps PrintGCCauses
  3. java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
  4. java -XX:+PrintFlagsInitial 默认参数值
  5. java -XX:+PrintFlagsFinal 最终参数值
  6. java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数
  7. java -XX:+PrintFlagsFinal -version |grep GC

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.shifpeng.scaffold.service.jvm;

import java.util.LinkedList;
import java.util.List;

/**
* @Project: scaffold
* @Description:
* @Author: Steven
**/
public class HelloGC {
public static void main(String[] args) {
System.out.println("HelloGC!");
List list = new LinkedList();
for(;;) {
byte[] b = new byte[1024*1024];
list.add(b);
}
}

}

以下为运行时打印的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-XX:InitialHeapSize=41943040 -XX:MaxHeapSize=62914560 -XX:MaxNewSize=10485760 -XX:NewSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
HelloGC!
# GC(指的是YGC) (GC产生的原因:内存分配失败) 整个堆经过一次回收之后从多大变化到多大 消耗了多长时间
[GC (Allocation Failure) 7307K->6760K(39936K), 0.0034057 secs]
[GC (Allocation Failure) 14088K->13899K(39936K), 0.0028386 secs]
[GC (Allocation Failure) 21384K->21032K(39936K), 0.0023978 secs]
[GC (Allocation Failure) 28354K->28200K(39936K), 0.0022355 secs]
[Full GC (Ergonomics) 28200K->28050K(54272K), 0.0066606 secs]
[GC (Allocation Failure) 35374K->35378K(54272K), 0.0021788 secs]
[GC (Allocation Failure) 42696K->42578K(54272K), 0.0028414 secs]
[Full GC (Ergonomics) 42578K->42386K(60416K), 0.0031029 secs]
[GC (Allocation Failure) 49708K->49714K(60416K), 0.0024475 secs]
[Full GC (Ergonomics) 49714K->49555K(60416K), 0.0026874 secs]
[Full GC (Ergonomics) 56871K->56723K(60416K), 0.0039821 secs]
[Full GC (Ergonomics) 57747K->57747K(60416K), 0.0031147 secs]
[Full GC (Allocation Failure) 57747K->57735K(60416K), 0.0056821 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.shifpeng.scaffold.service.jvm.HelloGC.main(HelloGC.java:17)

Process finished with exit code 1

由于每次GC都回收不了新产生的字节,因为都会扔到List中

Full GC 也搞不定了,内存溢出


如果使用-XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+PrintGC 查看CMS的日志

其实跟上面是差不多的,就多了CMS的几个阶段的日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=697933824 -XX:MaxTenuringThreshold=6 -XX:OldPLABSize=16 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 
HelloGC!
[GC (Allocation Failure) 69359K->67016K(253440K), 0.0248034 secs]
[GC (Allocation Failure) 135972K->135675K(253440K), 0.0277760 secs]
[GC (CMS Initial Mark) 136699K(253440K), 0.0001895 secs]
[GC (Allocation Failure) 204630K->204235K(276056K), 0.0248144 secs]
[GC (Allocation Failure) 273198K->272823K(344932K), 0.0251243 secs]
[GC (Allocation Failure) 341791K->341432K(412780K), 0.0264183 secs]
[GC (Allocation Failure) 410406K->410043K(481656K), 0.0282374 secs]
[GC (Allocation Failure) 479017K->478651K(550532K), 0.0263303 secs]
[GC (Allocation Failure) 547627K->547262K(619408K), 0.0305254 secs]
[GC (Allocation Failure) 616238K->615875K(687256K), 0.0291081 secs]
[GC (Allocation Failure) 684851K->684483K(756132K), 0.0273339 secs]
[GC (Allocation Failure) 753459K->753096K(825008K), 0.0399627 secs]
[GC (Allocation Failure) 822072K->821704K(892856K), 0.0287091 secs]
[GC (Allocation Failure) 890681K->890317K(961732K), 0.0256037 secs]
[GC (Allocation Failure) 959294K->958926K(1030608K), 0.0256964 secs]
[GC (Allocation Failure) 1027902K->1027538K(1099484K), 0.0248517 secs]
[GC (Allocation Failure) 1096515K->1096147K(1167332K), 0.0258776 secs]
[GC (Allocation Failure) 1165124K->1164758K(1236208K), 0.0252731 secs]
[GC (Allocation Failure) 1233734K->1233370K(1305084K), 0.0251292 secs]
[GC (Allocation Failure) 1302347K->1301979K(1373960K), 0.0268682 secs]
[GC (Allocation Failure) 1370955K->1370591K(1441808K), 0.0248420 secs]
[GC (Allocation Failure) 1439568K->1439202K(1510684K), 0.0272582 secs]
[GC (Allocation Failure) 1508179K->1507811K(1579560K), 0.0274426 secs]
[GC (Allocation Failure) 1576787K->1576421K(1648436K), 0.0270085 secs]
[GC (Allocation Failure) 1645398K->1645034K(1716284K), 0.0258314 secs]
[GC (Allocation Failure) 1714010K->1713642K(1785160K), 0.0267543 secs]
[GC (Allocation Failure) 1782619K->1782255K(1854036K), 0.0314271 secs]
[GC (Allocation Failure) 1851232K->1850864K(1922912K), 0.0306285 secs]
[GC (Allocation Failure) 1920216K->1918455K(1989732K), 0.0297121 secs]
[GC (Allocation Failure) 1987421K->1987063K(2058608K), 0.0288689 secs]
[GC (Allocation Failure) 2056034K->2055674K(2127484K), 0.0264972 secs]
[GC (Allocation Failure) 2124697K->2122434K(2194304K), 0.0285294 secs]
[GC (Allocation Failure) 2191523K->2190120K(2262152K), 0.0307858 secs]
[GC (Allocation Failure) 2259534K->2258806K(2330000K), 0.0314064 secs]
[GC (CMS Final Remark) 2259830K(2330000K), 0.0030712 secs]
[GC (Allocation Failure) 2328141K->2326411K(3591424K), 0.0330988 secs]
[GC (Allocation Failure) 2395368K->2394991K(3591424K), 0.0330699 secs]
[GC (Allocation Failure) 2463954K->2463601K(3591424K), 0.0301940 secs]
[GC (Allocation Failure) 2532569K->2532214K(3591424K), 0.0386445 secs]
[GC (Allocation Failure) 2601185K->2600824K(3591424K), 0.0410251 secs]
[GC (CMS Initial Mark) 2601848K(3591424K), 0.0001361 secs]
[GC (Allocation Failure) 2669797K->2669433K(3591424K), 0.0314408 secs]
[GC (Allocation Failure) 2738407K->2738043K(3591424K), 0.0618842 secs]
[GC (CMS Final Remark) 2739067K(3591424K), 0.0040706 secs]
[GC (Allocation Failure) 2806989K->2806623K(3591424K), 0.0846823 secs]
[GC (CMS Initial Mark) 2807647K(3591424K), 0.0001551 secs]
[GC (Allocation Failure) 2875598K->2875235K(3591424K), 0.0648956 secs]
[GC (Allocation Failure) 2944211K->2943844K(3591424K), 0.0762293 secs]
[GC (CMS Final Remark) 2944868K(3591424K), 0.0038512 secs]
[GC (Allocation Failure) 3012820K->3012456K(3591424K), 0.1002818 secs]
[GC (CMS Initial Mark) 3013480K(3591424K), 0.0002732 secs]
[GC (Allocation Failure) 3081433K->3081067K(3591424K), 0.1141234 secs]
[GC (Allocation Failure) 3150044K->3149676K(3591424K), 0.0989878 secs]
[GC (CMS Final Remark) 3150700K(3591424K), 0.0028736 secs]
[GC (Allocation Failure) 3218652K->3218288K(3591424K), 0.0516957 secs]
[GC (CMS Initial Mark) 3220704K(3591424K), 0.0002281 secs]
[GC (Allocation Failure) 3287265K->3286897K(3591424K), 0.0557199 secs]
[GC (Allocation Failure) 3355874K->3355510K(3591424K), 0.0611115 secs]
[GC (CMS Final Remark) 3356534K(3591424K), 0.0035585 secs]
[GC (Allocation Failure) 3424486K->3424118K(3591424K), 0.0541929 secs]
[GC (CMS Initial Mark) 3425142K(3591424K), 0.0001348 secs]
[GC (Allocation Failure) 3493095K->3492731K(3591424K), 0.0525449 secs]
[Full GC (Allocation Failure) 3561707K->3561321K(3591424K), 1.6774237 secs]
[Full GC (Allocation Failure) 4124114K->4123518K(4126208K), 0.1210706 secs]
[GC (CMS Initial Mark) 4124542K(4126208K), 0.0003247 secs]
[Full GC (Allocation Failure) 4125095K->4124542K(4126208K), 0.0106620 secs]
[Full GC (Allocation Failure) 4124542K->4124496K(4126208K), 1.7586102 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.shifpeng.scaffold.service.jvm.HelloGC.main(HelloGC.java:17)

Process finished with exit code 1

GC日志详解

已PS-PO GC为例:

image-20210524154540825

其中:total = eden + 1个survivor

其中Time

time ls real 总共占了多少时间 user:总共用户态用了多长时间;sys内核态占了多长时间

heap dump部分:

1
2
3
eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000)
后面的内存地址指的是,起始地址,使用空间结束地址,整体空间结束地址

image-20210524155117251

调优前的基础概念:

  1. 吞吐量:用户代码时间 /(用户代码执行时间 + 垃圾回收时间)即拿出多少时间干正经事
  2. 响应时间:STW越短,响应时间越好

所谓调优 ,首先确定,追求啥?吞吐量优先,还是响应时间优先?还是在满足一定的响应时间的情况下,要求达到多大的吞吐量…

比如:

科学计算、数据挖掘,thrput:吞吐量优先。

吞吐量优先的一般使用:(PS + PO)

响应时间优先:比如网站 GUI API (1.8 G1)

什么是调优

  1. 根据需求进行JVM规划和预调优
  2. 优化运行JVM运行环境(慢,卡顿)
  3. 解决JVM运行过程中出现的各种问题(OOM)

调优,从规划开始

  • 调优,从业务场景开始,没有业务场景的调优都是耍流氓

  • 无监控(压力测试,能看到结果),不调优

  • 步骤:

    1. 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)
      1. 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
      2. 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS]
    2. 选择回收器组合
    3. 计算内存需求(经验值 1.5G 16G)
    4. 选定CPU(越高越好)
    5. 设定年代大小、升级年龄
    6. 设定日志参数(生产环境一定不是一个日志文件)
      1. -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
      2. 或者每天产生一个日志文件
    7. 观察日志情况

预调优案例

案例1:垂直电商,最高每日百万订单,处理订单系统需要什么样的服务器配置?

这个问题比较业余,因为很多不同的服务器配置都能支撑(1.5G 16G)

1小时360000集中时间段, 100个订单/秒,(找一小时内的高峰期,1000订单/秒)

经验值,

非要计算:一个订单产生需要多少内存?512K * 1000 500M内存

专业一点儿问法:要求响应时间100ms

压测!

案例2:12306遭遇春节大规模抢票应该如何支撑?

12306应该是中国并发量最大的秒杀网站:

号称并发量100W最高

CDN -> LVS -> NGINX -> 业务系统 -> 每台机器1W并发(10K问题) 100台机器

普通电商订单 -> 下单 ->订单系统(IO)减库存 ->等待用户付款

12306的一种可能的模型: 下单 -> 减库存 和 订单(redis kafka) 同时异步进行 ->等付款

减库存最后还会把压力压到一台服务器

可以做分布式本地库存 + 单独服务器做库存均衡

大流量的处理方法:分而治之

怎么得到一个事务会消耗多少内存?

  1. 弄台机器,看能承受多少TPS?是不是达到目标?扩容或调优,让它达到
  2. 用压测来确定

优化环境

  1. 有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G 的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G 的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了
    1. 为什么原网站慢? 很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW长,响应时间变慢
    2. 为什么会更卡顿? 内存越大,FGC时间越长
    3. 咋办? PS 换成PN + CMS 或者 G1
  2. 系统CPU经常100%,如何调优?(面试高频) CPU100%那么一定有线程在占用系统资源,
    1. 找出哪个进程cpu高(top)
    2. 该进程中的哪个线程cpu高(top -Hp)
    3. 导出该线程的堆栈 (jstack)
    4. 查找哪个方法(栈帧)消耗时间 (jstack)
    5. 工作线程占比高 | 垃圾回收线程占比高
  3. 系统内存飙高,如何查找问题?(面试高频)
    1. 导出堆内存 (jmap)
    2. 分析 (jhat jvisualvm mat jprofiler … )
  4. 如何监控JVM
    1. jstat jvisualvm jprofiler arthas top…

解决JVM运行中的问题

一个案例理解常用工具

  1. 测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    package com.shifpeng.scaffold.service.jvm;

    import java.math.BigDecimal;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;

    /**
    * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
    * @Project: scaffold
    * @Description:
    * @Author: Steven
    **/
    public class FullGC_Problem01 {
    private static class CardInfo {
    BigDecimal price = new BigDecimal(0.0);
    String name = "张三";
    int age = 5;
    Date birthdate = new Date();

    public void m() {

    }
    }

    private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
    new ThreadPoolExecutor.DiscardOldestPolicy());

    public static void main(String[] args) throws Exception {
    executor.setMaximumPoolSize(50);

    for (;;){
    modelFit();
    Thread.sleep(100);
    }
    }

    private static void modelFit(){
    List<CardInfo> taskList = getAllCardInfo();
    taskList.forEach(info -> {
    // do something
    executor.scheduleWithFixedDelay(() -> {
    //do sth with info
    info.m();

    }, 2, 3, TimeUnit.SECONDS);
    });
    }

    private static List<CardInfo> getAllCardInfo(){
    List<CardInfo> taskList = new ArrayList<>();

    for (int i = 0; i < 100; i++) {
    CardInfo ci = new CardInfo();
    taskList.add(ci);
    }

    return taskList;
    }

    }

  2. java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.gc.T15_FullGC_Problem01

  3. 一般是运维团队首先受到报警信息(CPU Memory)

  4. top命令观察到问题:内存不断增长 CPU占用率居高不下

  5. top -Hp 观察进程中的线程,哪个线程CPU和内存占比高

  6. jps定位具体java进程 jstack 定位线程状况,重点关注:WAITING BLOCKED eg. waiting on <0x0000000088ca3310> (a java.lang.Object) 假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁 怎么找?搜索jstack dump的信息,找 ,看哪个线程持有这把锁RUNNABLE 作业:1:写一个死锁程序,用jstack观察 2 :写一个程序,一个线程持有锁不释放,其他线程等待

  7. 为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称 怎么样自定义线程池里的线程名称?(自定义ThreadFactory)
    image-20210525111856214

  8. jinfo pid

  9. jstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用) jstat -gc 4655 500 : 每个500个毫秒打印GC的情况 如果面试官问你是怎么定位OOM问题的?如果你回答用图形界面(错误)
    1:已经上线的系统不用图形界面用什么?(cmdline arthas)
    2:图形界面到底用在什么地方?测试!测试的时候进行监控!(压测观察)

  10. jmap - histo 4655 | head -20,查找有多少对象产生(这个命令还是可以用的,影响不是很大,所以一般不在线上用)arthas没有这个功能

  11. jmap -dump:format=b,file=xxx pid :(生产堆转储文件,但是这个命令对线上影响很大)

    线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)
    1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件
    2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
    3:在线定位(一般小点儿公司用不到)

  12. java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01

  13. 进行dump文件分析:
    使用MAT 、 jhat、jvisualvm
    进行dump文件分析 https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
    jhat -J-mx512M xxx.dump http://192.168.17.11:7000 拉到最后:找到对应链接 可以使用OQL查找特定问题对象

  14. 找到代码的问题


怎么定位CPU飙升、OOM

先通过top命令找到占用高的进程Id

再通过top -Hp 进程Id,查看该进程所有的线程,或者通过jps定位具体java进程

通过jstack可以找到一些线程中死锁的情况

比较麻烦的事OOM,OOM怎么定位呢?如果是测试的话,一般有图形化界面jconsole、jvisualVM,比如压测

生产的话,一般会用到jmap

jmap - histo 4655 | head -20,查找有多少对象产生

arthas

jconsole远程连接

  1. 程序启动加入参数:

    1
    java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
  2. 如果遭遇 Local host name unknown:XXX的错误,修改/etc/hosts文件,把XXX加入进去

    1
    2
    192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
  3. 关闭linux防火墙(实战中应该打开对应端口)

    1
    2
    service iptables stop
    chkconfig iptables off #永久关闭
  4. windows上打开 jconsole远程连接 192.168.17.11:11111

jvisualvm远程连接

https://www.cnblogs.com/liugh/p/7620336.html (简单做法)

jprofiler (收费)

arthas在线排查工具

  • 为什么需要在线排查? 在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。
  • jvm观察jvm信息
  • thread定位线程问题
  • dashboard 观察系统情况
  • heapdump + jhat分析
  • jad反编译 动态代理生成类的问题定位 第三方的类(观察代码) 版本问题(确定自己最新提交的版本是不是被使用)
  • redefine 热替换 目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性 m() -> mm()
  • sc - search class
  • watch - watch method
  • 没有包含的功能:jmap

GC算法的基础概念

  • Card Table 由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card 在结构上,Card Table用BitMap来实现

CMS

CMS的问题

  1. Memory Fragmentation

    -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction 默认为0 指的是经过多少次FGC才进行压缩

  2. Floating Garbage

    Concurrent Mode Failure 产生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped

    解决方案:降低触发CMS的阈值

    PromotionFailed

    解决方案类似,保持老年代有足够的空间

    –XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让CMS保持老年代足够的空间

CMS日志分析

执行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01

[GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

ParNew:年轻代收集器

6144->640:收集前后的对比

(6144):整个年轻代容量

6585 -> 2770:整个堆的情况

(19840):整个堆大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[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

  1. ▪https://www.oracle.com/technical-resources/articles/java/g1gc.html

image-20210526114830218

image-20210526114649724

G1日志详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[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]

image-20210526135013718

案例汇总

OOM产生的原因多种多样,有些程序未必产生OOM,不断FGC(CPU飙高,但内存回收特别少) (上面案例)

  1. 硬件升级系统反而卡顿的问题(见上)

  2. 线程池不当运用产生OOM问题(见上) 不断的往List里加对象(实在太LOW)

  3. smile jira问题
    实际系统不断重启
    解决问题 加内存 + 更换垃圾回收器 G1
    真正问题在哪儿?不知道

  4. tomcat http-header-size过大问题(Hector)(相当于每个请求来的时候都会占这么大的内存) 通过jmap查看对象的时候,发现了一个叫什么http的对象https://juejin.cn/post/6844903834142113805

  5. lambda表达式导致方法区溢出问题(MethodArea / Perm Metaspace) LambdaGC.java
    -XX:MaxMetaspaceSize=9M -XX:+PrintGCDetails

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    "C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -XX:MaxMetaspaceSize=9M -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\lib\idea_rt.jar=49316:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\work\ijprojects\JVM\out\production\JVM;C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar" com.mashibing.jvm.gc.LambdaGC
    [GC (Metadata GC Threshold) [PSYoungGen: 11341K->1880K(38400K)] 11341K->1888K(125952K), 0.0022190 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    [Full GC (Metadata GC Threshold) [PSYoungGen: 1880K->0K(38400K)] [ParOldGen: 8K->1777K(35328K)] 1888K->1777K(73728K), [Metaspace: 8164K->8164K(1056768K)], 0.0100681 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
    [GC (Last ditch collection) [PSYoungGen: 0K->0K(38400K)] 1777K->1777K(73728K), 0.0005698 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    [Full GC (Last ditch collection) [PSYoungGen: 0K->0K(38400K)] [ParOldGen: 1777K->1629K(67584K)] 1777K->1629K(105984K), [Metaspace: 8164K->8156K(1056768K)], 0.0124299 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:388)
    at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
    Caused by: java.lang.OutOfMemoryError: Compressed class space
    at sun.misc.Unsafe.defineClass(Native Method)
    at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:63)
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:394)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:393)
    at sun.reflect.MethodAccessorGenerator.generateSerializationConstructor(MethodAccessorGenerator.java:112)
    at sun.reflect.ReflectionFactory.generateConstructor(ReflectionFactory.java:398)
    at sun.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:360)
    at java.io.ObjectStreamClass.getSerializableConstructor(ObjectStreamClass.java:1574)
    at java.io.ObjectStreamClass.access$1500(ObjectStreamClass.java:79)
    at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:519)
    at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:494)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:494)
    at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:391)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at javax.management.remote.rmi.RMIConnectorServer.encodeJRMPStub(RMIConnectorServer.java:727)
    at javax.management.remote.rmi.RMIConnectorServer.encodeStub(RMIConnectorServer.java:719)
    at javax.management.remote.rmi.RMIConnectorServer.encodeStubInAddress(RMIConnectorServer.java:690)
    at javax.management.remote.rmi.RMIConnectorServer.start(RMIConnectorServer.java:439)
    at sun.management.jmxremote.ConnectorBootstrap.startLocalConnectorServer(ConnectorBootstrap.java:550)
    at sun.management.Agent.startLocalManagementAgent(Agent.java:137)
  6. 直接内存溢出问题(少见) 《深入理解Java虚拟机》P59,使用Unsafe分配直接内存,或者使用NIO的问题

  7. 栈溢出问题 -Xss设定太小
    写个程序造成栈溢出

  8. 比较一下这两段程序的异同,分析哪一个是更优的写法:第一个

    1
    2
    3
    4
    5
    6
    Object o = null;
    for(int i=0; i<100; i++) {
    //只有一个引用
    o = new Object();
    //业务处理
    }
    1
    2
    3
    4
    for(int i=0; i<100; i++) {
    Object o = new Object();
    //每次一个新的对象,指向一个新的o,有可能造成很多回收不掉的
    }
  9. 重写finalize引发频繁GC 小米云,HBase同步系统,系统通过nginx访问超时报警,最后排查,C++程序员重写finalize(C++finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它finalize()方法,让此对象处理它生前的最后事情(这个对象可以趁这个时机挣脱死亡的命运))引发频繁GC问题 为什么C++程序员会重写finalize?(new delete) finalize耗时比较长(200ms)

  10. 如果有一个系统,内存一直消耗不超过10%,但是观察GC日志,发现FGC总是频繁产生,会是什么引起的?
    System.gc() (这个比较Low)

  11. Distuptor有个可以设置链的长度,如果过大,然后对象大,消费完不主动释放,会溢出 (来自 死物风情)

  12. 用jvm都会溢出,mycat用崩过,1.6.5某个临时版本解析sql子查询算法有问题,9个exists的联合sql就导致生成几百万的对象(来自 死物风情)

  13. new 大量线程,会产生 native thread OOM,(low)应该用线程池, 解决方案:减少堆空间(太TMlow了),预留更多内存产生native thread JVM内存占物理内存比例 50% - 80%

设计架构上的两大重要思想

1、分而治之

2、分层

GC常用参数

  • -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
  • -XX:+UseTLAB 使用TLAB,默认打开
  • -XX:+PrintTLAB 打印TLAB的使用情况
  • -XX:TLABSize 设置TLAB大小
  • -XX:+DisableExplictGC 作用就是让System.gc()不管用 ,FGC
  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintHeapAtGC
  • -XX:+PrintGCTimeStamps
  • -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 逃逸分析 标量替换 … 这些不建议设置

Parallel常用参数

  • -XX:SurvivorRatio
  • -XX:PreTenureSizeThreshold 大对象到底多大
  • -XX:MaxTenuringThreshold
  • -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
  • -XX:+UseAdaptiveSizePolicy 自动选择各区大小比例

CMS常用参数

  • -XX:+UseConcMarkSweepGC
  • -XX:ParallelCMSThreads CMS线程数量
  • -XX:CMSInitiatingOccupancyFraction 使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
  • -XX:+UseCMSCompactAtFullCollection 在FGC时进行压缩
  • -XX:CMSFullGCsBeforeCompaction 多少次FGC之后进行压缩
  • -XX:+CMSClassUnloadingEnabled
  • -XX:CMSInitiatingPermOccupancyFraction 达到什么比例时进行Perm回收
  • GCTimeRatio 设置GC时间占用程序运行时间的百分比
  • -XX:MaxGCPauseMillis 停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

G1常用参数

  • -XX:+UseG1GC
  • -XX:MaxGCPauseMillis 建议值,G1会尝试调整Young区的块数来达到这个值
  • -XX:GCPauseIntervalMillis ?GC的间隔时间
  • -XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长 ZGC做了改进(动态区块大小)
  • G1NewSizePercent 新生代最小比例,默认为5%
  • G1MaxNewSizePercent 新生代最大比例,默认为60%
  • GCTimeRatio GC时间建议比例,G1会根据这个值调整堆空间
  • ConcGCThreads 线程数量
  • InitiatingHeapOccupancyPercent 启动G1的堆空间占用比例

作业

  1. -XX:MaxTenuringThreshold控制的是什么? A: 对象升入老年代的年龄 B: 老年代触发FGC时的内存垃圾比例

  2. 生产环境中,倾向于将最大堆内存和最小堆内存设置为:(为什么?) A: 相同 B:不同

  3. JDK1.8默认的垃圾回收器是: A: ParNew + CMS B: G1 C: PS + ParallelOld D: 以上都不是

  4. 什么是响应时间优先?

  5. 什么是吞吐量优先?

  6. ParNew和PS的区别是什么?

  7. ParNew和ParallelOld的区别是什么?(年代不同,算法不同)

  8. 长时间计算的场景应该选择:A:停顿时间 B: 吞吐量

  9. 大规模电商网站应该选择:A:停顿时间 B: 吞吐量

  10. HotSpot的垃圾收集器最常用有哪些?

  11. 常见的HotSpot垃圾收集器组合有哪些?

  12. JDK1.7 1.8 1.9的默认垃圾回收器是什么?如何查看?

  13. 所谓调优,到底是在调什么?

  14. 如果采用PS + ParrallelOld组合,怎么做才能让系统基本不产生FGC

  15. 如果采用ParNew + CMS组合,怎样做才能够让系统基本不产生FGC

    1.加大JVM内存

    2.加大Young的比例

    3.提高Y-O的年龄

    4.提高S区比例

    5.避免代码内存泄漏

  16. G1是否分代?G1垃圾回收器会产生FGC吗?

  17. 如果G1产生FGC,你应该做什么?

    1. 扩内存
    2. 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
    3. 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)
  18. 问:生产环境中能够随随便便的dump吗? 小堆影响不大,大堆会有服务暂停或卡顿(加live可以缓解),dump前会有FGC

  19. 问:常见的OOM问题有哪些? 栈 堆 MethodArea 直接内存

参考资料

  1. https://blogs.oracle.com/jonthecollector/our-collectors

  2. https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

  3. http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

  4. JVM调优参考文档:https://docs.oracle.com/en/java/javase/13/gctuning/introduction-garbage-collection-tuning.html#GUID-8A443184-7E07-4B71-9777-4F12947C8184

  5. https://www.cnblogs.com/nxlhero/p/11660854.html 在线排查工具

  6. https://www.jianshu.com/p/507f7e0cc3a3 arthas常用命令

  7. Arthas手册:

    1. 启动arthas java -jar arthas-boot.jar
    2. 绑定java进程
    3. dashboard命令观察系统整体情况
    4. help 查看帮助
    5. help xx 查看具体命令帮助
  8. jmap命令参考:

    https://www.jianshu.com/p/507f7e0cc3a3

    1. jmap -heap pid
    2. jmap -histo pid
    3. jmap -clstats pid

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !