JVM底层原理及调优之笔记一

JVM底层原理及调优

1.java虚拟机内存模型(JVM内存模型)

JVM底层原理及调优之笔记一_第1张图片

1.堆(-Xms -Xmx -Xmn)
java堆,也称为GC堆,是JVM中所管理的内存中最大的一块内存区域,是线程共享的,在JVM启动时创建。存放了对象的实例及数组(所有new的对象),
无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中;
2.线程栈 每个线程存在一个独立的线程栈内存区域,每个线程栈内存中会为每个方法创建各自的栈帧,栈帧中包含:局部变量表、操作数栈、动态链接、方法出口等; 栈中存放:方法调用过程中基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量; 其中64位长度的long和double类型的数据会占用两个局部变量的空间,其余数据类型只占一个;
3.方法区(元空间/永久代) 存放常量、静态变量、类信息、即时编译器后的代码,运行时常量池:方法区的一部分,Class文件中除了有类的版本,字段,方法,接口等描述信息外, 还有一项信息就是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中, 元空间取代永久代,隔离堆和元空间的垃圾回收,避免频繁Full GC以及OOM等问题;
4.程序计数器 当前线程所执行的字节码的行号指示器,程序计数器的意义: 1.确保多线程程序的正常运行 2.java是多线程的,意味着线程存在上下文切换

2.GC的基础知识

2.1  什么是垃圾? 没有任何引用指向的一个对象或者多个对象(循环引用)

2.2 如何定位垃圾? 引用计数和roots可达性分析 (java使用的是根可达性分析算法)

在Java语言中,GC Roots包括:
线程栈变量
静态变量
常量池
JNI指针

2.3 GC垃圾回收算法比较

GC回收算法比较

特性

标记清除算法

位置分散,容易产生碎片

复制算法

浪费空间

标记压缩算法

效率低(适用老年代)

分代收集算法

算法搭配灵活(new-复制,old-标记压缩)

2.4 新生代 = Eden + 2个survivor区 new:old = 1: 2

2.5 新生代、老年代数据流转大致过程

新生代 = Eden + 2个survivor区 
   1. YGC回收之后,大多数的对象会被回收,活着的进入s0
   2. 再次YGC,活着的对象eden + s0 -> s1
   3. 再次YGC,eden + s1 -> s0
   4. 年龄足够 -> 老年代 (15 CMS 6)
   5. s区装不下 -> 老年代
 老年代
   1. 顽固分子
   2. 老年代满了FGC Full GC
GC回收
1.尽量减少FGC
2 minorGC = YoungGC
3 majorGC = FullGC

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

  a.调用System.gc时,系统建议执行Full GC,但是不必然执行

  b.老年代空间不足

  c.方法去空间不足

  d.通过Minor GC后进入老年代的平均大小大于老年代的可用内存

e.由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

2.6 常见的垃圾回收器

JVM底层原理及调优之笔记一_第2张图片

1. Serial 年轻代 串行回收 GC线程回收,其他线程暂停
2. PS 年轻代 并行回收
3. ParNew 年轻代 配合CMS的并行回收 Serial收集器新生代的并行版本
4. SerialOld 
5. ParallelOld
6. ConcurrentMarkSweep(CMS) 老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms)
CMS问题较多,一般都需要手动指定。CMS采用标记清除算法回收,所以就会存在产生碎片的问题,当老年代分配的对象分配不下的情况,需要配合SerialOld进行老年代的回收 7. G1(10ms) 8. ZGC (1ms) 相当于 C++ 9. Shenandoah 10.Eplison

1.8默认的垃圾回收:PS + ParallelOld

3.GC调优准备

3.1 了解生产环境下的垃圾回收器组合 打印JVM内存信息 java -XX:+PrintCommandLineFlags

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

* JVM参数分类
> 标准: - 开头,所有的HotSpot都支持 > 非标准:-X 开头,特定版本HotSpot支持特定命令 > 不稳定:-XX 开头,下个版本可能取消

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

 JVM底层原理及调优之笔记一_第3张图片

* 常见垃圾回收器组合参数设定:(1.8)
  * -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
    * 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
  * -XX:+UseParNewGC = ParNew + SerialOld
    * 这个组合已经很少用(在某些版本中已经废弃)
    * https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future
  * UseConcMarkSweepGC = ParNew + CMS + Serial Old
  * UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
  * UseParallelOldGC = Parallel Scavenge + Parallel Old
  * UseG1GC = G1

常用的GC组合

参数

描述

UseSerialGC

Serial New+ Serial Old

UseParNewGC

ParNew+Serial Old(rarely dispose)

UseConcMarkSweepGC

ParNew+CMS+Serial Old

UseParallelGC/UseParallelOldGC

Parallel Scavenge+Parallel Old(1.8默认)

UseG1GC

G1

2.3 JVM调优的步骤

* 步骤:
  1. 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)
     1. 响应时间、停顿时间
     2. 吞吐量 = 用户时间 / 用户时间 + GC时间
 响应时间和吞吐量无法同时调优  响应时间快 == GC停顿时间短
  2. 选择回收器组合
  3. 计算内存需求
  4. 设定年代大小、升级年龄
  5. 设定日志参数
     1. -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
  6. 观察日志情况

4.GC日志查看

通过在java命令种加入参数来指定对应的gc类型,打印gc日志信息并输出至文件等策略

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

2019-11-19T11:50:42.352+0800[当前时间戳]: 0.169[时间戳]: [GC[Young GC (Allocation Failure) [PSYoungGen: 63744K[回收前年轻代大小]->9680K[回收后年轻代大小](74240K)总年轻代大小] 63744K回收前堆大小->62928K回收后堆大小(243712K分配堆的总大小), 0.0092841【消耗总时间】 secs] [Times: user=0.02【用户花费时间】 sys=0.03【系统花费时间】,real =0.01 secs [总消耗时间]】

2019-11-19T11:50:42.369+0800: 0.186: [GC (Allocation Failure) [PSYoungGen: 73412K->9728K(138240K)] 126661K->126473K(307712K), 0.0106607 secs [Times: user=0.03 sys=0.03, real=0.01 secs】

2019-11-19T11:50:43.326+0800: 1.142: [Full GC (Ergonomics) [PSYoungGen: 798012K->797708K(808960K)] [ParOldGen: 2708855K->2708855K(2708992K)] 3506867K->3506564K(3517952K), [Metaspace: 2735K->2735K(1056768K)], 0.0182299 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 

2019-11-19T11:50:43.344+0800: 1.160: [Full GC (Allocation Failure) [PSYoungGen: 797708K->797708K(808960K)] [ParOldGen: 2708855K->2708844K(2708992K)] 3506564K->3506552K(3517952K), [Metaspace: 2735K->2735K(1056768K)], 0.6587112 secs] [Times: user=6.22 sys=0.02, real=0.66 secs]

Heap
PSYoungGen total 808960K, used 798720K [0x000000076d580000【起始地址】, 0x000000079fa00000【占用空间结束地址】, 0x00000007c0000000【整体空间结束地址】)
eden space 798720K, 100% used [0x000000076d580000,0x000000079e180000,0x000000079e180000)
from space 10240K, 0% used [0x000000079eb00000,0x000000079eb00000,0x000000079f500000)
to space 9728K, 0% used [0x000000079e180000,0x000000079e180000,0x000000079eb00000)
ParOldGen total 2708992K, used 2708845K [0x00000006c8000000, 0x000000076d580000, 0x000000076d580000)
object space 2708992K, 99% used [0x00000006c8000000,0x000000076d55b440,0x000000076d580000)
Metaspace used 2770K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 264K, capacity 386K, committed 512K, reserved 1048576K

你可能感兴趣的:(JVM底层原理及调优之笔记一)