第二章-JVM线上案例分析

JVM垃圾回收时如何确定垃圾?

什么是垃圾

简单的说就是内存中已经不再被使用到的空间就是垃圾。

要进行垃圾回收,如何判断一个对象是否可以被回收?

  1. 引用计数法

    image-20191107135041445

  2. 枚举根节点做可达性分析(根搜索路径)

    image-20191107135052329

    案例:

    image-20191107135119813

    Java中可以作为GCRoots的对象:

    • 虚拟机栈 (栈帧中本地变量)中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(Native)引用的对象

如何盘点查看JVM默认值

JVM的参数类型

标配参数

  • -version
  • -help
  • java -showversion

X参数

  • -Xint:解释执行
  • -Xcomp:第一次使用就编译成本地代码
  • -Xmixed:混合模式

XX参数

  1. Boolean类型

    公式:-XX:+或者-某个属性值

    是否打印GC回收细节:-XX:+PrintGCDetails

    是否使用串行垃圾回收器:-XX:+PrintCommandLineFlags

  2. KV设值类型

    公式:-XX:属性key=属性值value

    案例:

    -XX:MetaspaceSize=128m

    -XX:MaxTernuringThreshold

  3. 其它

    image-20191107135529325

盘点家底查看JVM默认值

  1. -XX:+PrintFlagsInital

    查看初始默认值。

    公式:

    • java -XX:+PrintFlagsInital version
    • java -XX:+PrintFlagsInital

    案例:

    image-20191107135949381

  2. -XX:+PrintFlagsFinal

  3. PrintFlagsFinal举例,运行java的同时打印参数

  4. -XX:+PrintCommandLineFlags

    查看默认垃圾收集器。

    image-20191107140014832

工作用过的JVM常用基本配置参数

基础知识复习

image-20191107140212106

常用参数

image-20191107140259138

典型设置案例:

-Xms128m -Xmx4096 -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC

强、弱、软、虚引用分别是什么?

整体架构

image-20191107140452601

强引用(默认支持模式)

image-20191107140516522

案例:

image-20191107140529680

软引用

image-20191107140604482

案例:

image-20191107140615889

弱引用

案例:

image-20191107140640395

软引用与弱引用适用场景:

image-20191107140705479

你知道弱引用,能谈谈WeakHashMap吗

image-20191107140732376

虚引用

GCRoots和四大引用总结

image-20191107140814322

谈谈你对OMM的认识

  1. java.lang.StackOverFlowExceptioin

    image-20191107140939735

  2. java.lang.OutOfMemoryException:Java heap space

    image-20191107140949807

  3. java.lang.OutOfMemoryException:GC overhead limit exceeded

    image-20191107140959835

  4. java.lang.OutOfMemoryException:Direct buffer memory

    image-20191107141011179

  5. java.lang.OutOfMemoryException:unable to create new native thread

    image-20191107141021808

    服务器级别参数调优:

    image-20191107141038468

  6. java.lang.OutOfMemoryException:Metaspace

    Java以后的JVM:

    image-20191107141056572

    案例:

    image-20191107141122690

GC回收算法与垃圾回收器的关系?

  1. GC算法(引用计数/复制/标清/标整)是内存回收的方法论,垃圾收集器就是算法落地实现。

  2. 因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最合适的收集器,进行分代收集

  3. 4种主要的垃圾收集器

    image-20191107141258875

    image-20191107141234365

生产上你是如何配置垃圾收集器的?

怎么查看默认垃圾回收器是哪个

java -XX:+PrintCommandLineFlags -version

image-20191107141429408

默认垃圾回收器有哪些?

  • UseSerialGC
  • UseParalelGC
  • UseConcMarkSweepGC
  • UseParNewGC
  • UseParallelOldGC
  • UseG1GC

垃圾收集器

image-20191107141544236

参数预先说明

image-20191107141643259

Server/Client模式分别是什么意思

image-20191107141733417

新生代

  1. 串行GC(Serial)/(Serial Copying)

    image-20191107141813869

  2. 并行GC(ParNew)

    image-20191107141823653

  3. 并行回收GC(Parallel)/(Parallel Scavenge)

    image-20191107141835782

老年代

  1. 串行GC。

  2. 并行GC(Parallel Old)/(Parallel MSC)

    image-20191107142028848

  3. 并发标记清除GC(CMS)

    4个步骤:

    image-20191107142133689

    • 初始化标记(CMS init Mark)
    • 并发标记(CMR concurrent mark)和用户线程一起
    • 重新标记
    • 并发清除

    优缺点:

    • 优点:并发收集,低停顿
    • 缺点:并发执行对CPU压力大
    • 缺点:采用标记清除算法会导致大量碎片

案例

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
/**
* @desc
* 1.
* 2
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC (ParNew[par new generation] + Tenured[tenured generation])
* 备注情况:Java HotSpot(TM) 64-Bit Server VM warning:
* Using the ParNew young collector with the Serial old collector is deprecated
* and will likely be removed in a future release
*
* 3
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC (PSYoungGen + ParOldGen)
*
* 4
* 4.1
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC (PSYoungGen + ParOldGen)
* 4.2 不加就默认UseParallelGC
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags
*
* 5
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC (parNew + concurrent mark-sweep generation)
*
* 6
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC (garbage-first heap)
*
* 7(理论知道即可,实际中已经被优化掉了,没有了。)
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
*
* @Author xw
* @Date 2019/8/28
*/
public class GCDemo {
public static void main(String[] args) {
String str = "aaa";
System.out.println("GC test");
str = null;
System.gc();
}
}

垃圾收集器配置代码总结

image-20191107142349883

G1垃圾收集器

以前垃圾回收器的特点

  • 年轻代和老年代是各自独立连续的内存块
  • 年轻代收集使用单eden+S0+S1进行复制算法
  • 老年代收集必须扫描整个老年代区域
  • 都是以尽可能少而快地执行GC为设计原则

G1是什么

image-20191107142515620

特点:

image-20191107142545907

底层原理

  1. Region区域化垃圾收集器

    image-20191107142632872

  2. 回收步骤

    image-20191107142644124

  3. 4步过程

    image-20191107142653123

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @desc
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseG1GC
* @Author xw
* @Date 2019/8/28
*/
public class G1Demo {
public static void main(String[] args) {
String str = "abc";
while (true) {
str += str + new Random().nextInt(77777777) + new Random().nextInt(88888888);
str.intern();
}
}
}

常用配置参数

开发人员仅仅需要声明以下参数即可:

三步归纳:开始G1+设置最大内存+设置最大停顿时间

-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=n:最大GC停顿时间单位毫秒,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间

和CMS相比的优势

  • G1不会产生内存碎片
  • 是可以精确控制停顿。该收集器是把整个堆(新生代、老年代)划分多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。

生产环境服务器变慢,诊断思路和性能评估谈谈?

整机:top

update,系统命令的精简版。

CPU:vmstat

  1. 查看CPU(包含不限于)

    image-20191107143033185

  2. 查看额外

    image-20191107143042588

内存:free

  1. 应用程序可用内存数。

    image-20191107143133049

  2. 查看额外

    pidstat -p 进程号 -r 采样间隔秒数

硬盘:df

查看磁盘剩余空间。

image-20191107143201594

磁盘IO:iostat

  1. 磁盘IO性能评估

    image-20191107143236114

  2. 查看额外

    pidstat -p 进程号 -r 采样间隔秒数

网络IO:ifstat

  1. 默认本地没有,下载ifstat

    image-20191107143339451

  2. 查看网络IO

    image-20191107143350158

假如生产环境出现CPU占用过高,请谈谈你的分析思路和定位

  1. 先用top命令找出CPU占比最高的

  2. ps -ef或者jps进一步定位进程

  3. 定位到具体线程或代码

    ps -mp 进程 -o THREAD,tid,time

    • -m 显示所有的线程
    • -p pid 进程使用cpu的时间
    • -o 该参数后是用户自定义格式

    image-20191107143553680

  4. 将需要的线程ID转换为16进制格式(英文小写格式)

    printf “%x\n” 有问题的线程ID

  5. jstatck 进程ID | grep tid(16进制线程ID小写英文)-A60

    image-20191107143657206

JVM性能监测工具

https://docs.oracle.com/javase/8/docs/technotes/tools/