JVM

基础 #

定义
    JVM规范说明书, JVMS(Java Virtual Machine Specifications)
    Java语言规范, JLS(Java Language Specification)
    虚拟机
        指令集
        内存管理
过程
    x.java -> javac -> x.class -> ClassLoader -> (字节码解释器、JIT) -> 执行引擎
JVM语言
    Scala, Kotlin, Groovy, Clojure, jython, jruby等100多个
JVM实现
    HotSpot                 # Oracle官方, 8之后收费, OpenJDK为开源版本
    Jrockit                 # Oracle收购, 合并入HotSpot
    J9                      # IBM
    Microsoft VM
    TaobaoVM                # HotSpot深度定制
    LiquidVM                # 直接匹配专门硬件
    azul zing               # 收费,快, GC1mm, HotSpot参考写的G1
JRE, JDK
    JRE = jvm + core lib
    JDK = JRE + development kit

JVM构造 #

指标 #

吞吐量: 代码时间 / (代码时间 + GC时间)
响应时间: STW越短, 响应时间越好

指令(Instructions) #

分类
    基于栈的指令类          # 简单, HotSpot
    基于寄存器的指令集      # 复杂但快, HotSpot局部变量表
8大原子操作(JSR-133已放弃这个描述,但指令没变化)
    lock                    # 主内存,标识变量线程独占
    unlock                  # 主内存,解锁独占
    read                    # 主内存,读到工作内存
    load                    # 工作内存,read后的值放入线程本地变量副本
    use                     # 工作内存,传值给执行引擎
    assign                  # 工作内存,执行引擎结果赋值给线程本地变量 
    store                   # 工作内存,存值到主内存给write备用
    write                   # 主内存,写变量值
方法指令                    # 在methods的Code中罗列
    aload_0                 # 变量表第0项入栈
    invokespecial #1        # 调private(无多态)的方法
    invokevirtual           # 调有多态可能性的方法
    invokestatic            # 调静态方法
    invokeinterface         # 调interface方法
    invokedynamic           # 1.7加入,定义类似函数指针时生成(但每个函数都创建了类)
        调用动态产生的类
            lambda
            反射
            scala等JVM语言
            CGLib ASM
        组成
            bootstrapMethod
            方法签名
        <1.8的bug           # 类产生于Perm Space,内存不回收
            for(;;) {I j = C::n;}
    return                  # 方法返回
    bipush 8                # byte扩展成int类型,放到方法栈中
    sipush 200              # short
    istore_1                # 出栈,放到下标为1的局部变量表
    iload_1                 # 局部变量表下标1位置值压栈
    iinc 1 by 1             # 局部变量表1位置值+1
    iadd                    # 出栈两个,相加压栈
    new                     # new对象, 地址压栈
    dup                     # 复制栈顶并压栈
    pop                     # 弹出栈顶
    if_icmpne 7             # int值比较,不等时跳到第7条指令
    mul                     # 乘法
    sub                     # 减法

class结构 #

工具 #

javap -v a.class
jetbrain jclasslib
jetbrain BinEd
JBE                         # 可编辑

二进制 #

Magic Number(4字节)
    cafe babe
Minor Version(2字节)        # 小版本
Major Version(2字节)        # 大版本
    JDK1.7是51.0
    JDK1.8是52.0
constant_pool_count(2字节)
    # 长度constant_pool_count-1的表
constant_pool               # 索引、tag、类型
    1 CONSTANT_Utf8_info                        # 存一些描述字符串
    2 标记
    3 CONSTANT_Integer_info
    4 CONSTANT_Float_info
    5 CONSTANT_Long_info
    6 CONSTANT_Double_info
    7 CONSTANT_Class_info
    8 CONSTANT_String_info
    9 CONSTANT_Fieldref_info                
    10 CONSTANT_Methodref_info                  # 方法引用
        指向CONSTANT_Class_info
        指向CONSTANT_NameAndType_info
    11 CONSTANT_InterfaceMethodref_info
    12 CONSTANT_NameAndType_info                # 方法名与类型
    15 CONSTANT_MethodHandle_info
    16 CONSTANT_MethodType_info
    18 CONSTANT_InvokeDynamic_info
access_flags(2字节)         # bitmap按位与组合
    # class的修饰符
    ACC_PUBLIC 0x0001 public
    ACC_FINAL 0x0010 final
    ACC_SUPER 0x0020 JDK1.0.2之后必须为真, 表示invokespectial用新语义
    ACC_INTERFACE 0x0200 是否接口
    ACC_ABSTRACT 0x0400 抽象类
    ACC_SYNTHETIC 0x1000 编译器自动生成
    ACC_ANNOTATION 0x2000 
    ACC_ENUM 0x2000 
this_class(2字节)
    存名字对应指向常量池序号
super_class(2字节)
    存名字对应指向常量池序号
interfaces_count(2字节)
interfaces
fields_count(2字节)
fields
    access_flags(2字节)
    name_index              # 存常量池索引
    descriptor_index
        byte B
        char C
        double D
        float F
        int I
        long L
        short S
        boolean Z
        void V
        Object Ljava/lang/Object
        数组
            一维数组 [B
            多维数组 [[C
    attributes_count        # 赋加属性
    attributes
methods_count(2字节)
methods
    access_flags(2字节)
    name_index
    descriptor_index        # 先参数列表,后返回值
        void m() -> ()V
        String toString() -> Ljava/lang/String;
    attributes_count
    attributes              # 赋加属性
        Code                # 指令列表, 一般先压栈this(aload_0)
            LineNumberTable
            LocalVariableTable
attributes_count(2字节)
attributes

Agent #

例子
    打包 a.jar
        MANIFEST.MF
            Premain_Class: MyAgent
        public class MyAgent {
            public static Instrumentation inst;
            public static void premain(String agentArgs, Instrumentation _inst) {
                inst = _inst;
            } 
        }
    JVM参数 -javaagent: a.jar
    使用 MyAgent.inst

JMM(Java Memory Model) #

内存 #

运行时区域 #

# Runtime data areas
分类
    Program Counter             # 程序计数器,下一条指令位置
    Method Area                 # 方法区,线程间共享
        存储
            Class元信息
            代码编译信息, JIT编译信息
            常量池(Runtime Constant Pool)           # 常量池在运行时存放区
        版本区别
            Perm Space(<1.8)    # 要设定大小, 会溢出报错
                存字符串常量
                lambda生成临时类永远存在
                Full GC不清理
            Meta Space(>=1.8)   # 自动大小无上限
                字符串常量位于堆
                会触发Full GC
    JVM stacks                  # 线程栈 
        Frame(栈帧)             # 一个方法一个栈帧
            Local Variable Table                # 局部变量表, 方法内的局部变量,值在常量池
                默认第0个为this
            Operand Stack                       # 操作数栈
            Dynamic Linking                     # 指向调用方法的 运行时常量池的符号连接
            return address                      # 当前方法执行完的返回地址
    Native Method Stacks        # C/C++方法栈
    Direct Memory               # 直接内存
    Heap                        # 堆, 线程间共享

屏障 #

CPU屏障
JVM规范
    LoadLoad                # 上load和下load不能重排
    StoreStore
    LoadStore
    StoreLoad               # 最强

对象 #

对象内存存储 #

普通对象
    对象头: markword 8字节
    ClassPointer            # 指向Class对象, 启用压缩4字节,不启用8字节
    实例数据
        引用类型            # 启用压缩4字节,不启用8字节
    Padding: 对齐8的倍数
数组对象
    对象头
    ClassPointer
    数组长度4字节
    数组数据
    Padding
对象头
    # 32位64位(25位没用到),内容不同
    锁标志位2位             # 根据锁标志位判断存储内容
        01 无锁/偏向锁
        00 轻量级锁
        10 重量级锁
        11 GC标记
    是否偏向锁1位
    剩余位 
        无锁状态
            对象hashCode(25位或31位)
                没重写过时默认计算(System.identityHashCode())
                重写过的hashCode()结果不存在这里
            分代年龄
        轻量级锁
            指向栈中锁记录的指针
        重量级锁
            指向互斥量(重量级锁)的指针
        偏向锁
            线程ID 23位
            Epoch 2位
            分代年龄4位(所以分代年龄只有15)
    其它问题
        对象计算过hashCode,对象不能进入偏向锁状态(位已经被占了)
实验工具 javaagent

对象定位 #

句柄池                      # 指向句柄,句柄有对象指针和class指针, 三色标记GC提高效率
直接指针                    # 指向对象,对象指class, HotSpot使用

并发 #

硬件层数据一致性 #

硬件结构
    L0寄存器                # 1 cycles
    L1高速缓存              # 3-4 cycles, 1ns
    L2高速缓存              # 10 cycles, 3ns
    L3高速缓存              # 40-45 cycles, 15ns, 在主板
    (QPI总线传输)           # 20ns
    L4主存                  # 60-80ns
    L5磁盘
    L6远程文件存储
数据不一致                  # 从L2多CPU开始
    锁总线(bus lock)
    CPU缓存一致性协议(如intel MESI)

volatile #

工具
    hsdis                   # HotSpot Dis Assembler, 虚拟机字节码对应汇编
bytecode
    ACC_VOLATILE
JVM
    StoreStoreBarrier
    volatile写操作          # 上边写完再写,写完下边再读,写一致
    StoreLoadBarrier

    LoadLoadBarrier
    volatile读操作          # 上边读完再读,读完下边再写,读一致
    LoadStoreBarrier
OS
    windows
        lock
    linux
        上下屏障,最后lock

synchronized #

bytecode
    方法修饰
        synchronized
    代码
        monitorenter
        monitorexit
JVM
    C/C++实现,会调用OS的同步机制
OS
    lock

happens-before原则 #

# Java要求指令不能重排的几种情况

as if serial #

# 不管如何重排序,单线程执行结果不变

过程 #

编译 #

过程
    代码 -> bytecode -> JVM指令 -> OS指令
解释器(bytecode intepreter)
JIT(Just In-Time compiler)
混合模式
    解释器 + 热点代码编译
    热点代码检测
        方法计数器
        循环计数器

加载 #

HotSpot C++代码加载
    class对象加载到MethodArea
        metaspace(JDK1.8)
        permGeneration(JDK1.8之前)
class加载过程
    loading                 # 读到内存
    linking
        verification        # 校验
        preparation         # 静态变量赋默认值
        resolution          # 解析, loadClass()可指定是否解析。常量池的符号引用转换成内存地址引用
    initializing            # 静态变量赋初始值,执行静态代码
对象加载
    new过程
        class加载
        申请对象内存
        成员变量赋默认值
        调用构造方法<init>
            成员变量顺序赋初始值
            执行构造方法语句(先super)
双亲委派                
    过程
        类名一层层向上找
        找不到时,一层层找再向下委派找
        都不能加载时, 抛ClassNotFound
    为什么
        安全, 自定义类不能覆盖
        已加载不用重复加载
    父加载器
        不是类加载器的加载器
        不是父类
        是组合的parent对象
    打破
        为什么                    
            JDK1.2之前都重写loadClass()
            thread.setContextClassLoader()指定线程上下文classLoader
            热启动/热部署(OSGi tomcat)加载同一类不同版本
        做法
            重写loadClass(), new多个ClassLoader
类加载器
    Bootstrap               # 加载核心类 lib/rt.jar charset.jar等, C++实现所以get时为null
        如加载String
    Extension               # 加载扩展jar jre/lib/ext/*.jar, 由-Djava.ext.dirs指定
    App                     # 加载classpatch指定内容
    Custom ClassLoader      # 自定义ClassLoader
加载路径环境变量            # 来自Launcher源码
    Bootstrap.ClassLoader   sun.boot.class.path
    ExtensionClassLoader    java.ext.dirs
    AppClassLoader          java.class.path
API
    Class
        getClassLoader()
    ClassLoader             # findInCache() -> parent.loadClass() -> findClass()
        private final ClassLoader parent
        loadClass           # 热加载
    Launcher
        $AppClassLoader
        $ExtClassLoader
自定义类加载器
    class MyClassLoader extends ClassLoader {
        @Override
        Class findClass(String) {
            return defineClass()
        }
    }
懒加载                      # JVM未规定什么时候加载,但规定了什么时候初始化
初始化
    new getstatic putstatic invokestatic指令,访问final变量除外
    java.lang.reflect对类进行反射调用
    初始化子类时,父类首先初始化
    虚拟机启动时,被执行的主类
    动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic, REF_putstatic, REF_invokestatic的方法句柄时, 该类初始化

GC #

引用方式(强软弱虚) #

软引用
    # 内存不够用时回收,用做缓存
    # -Xms20M -Xmx20M
    SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
    System.gc();
    m.get();
    new byte[1024*1024*15]
    m.get();
弱引用
    # 只要gc就回收,用做容器如WeakHashMap(key是弱引用), ThreadLocalMap的key
    WeakReference<M> m = new WeakReference<>(new M());
    System.gc();
    m.get();
虚引用
    # 值被回收时放入队列来通知, 用来触发回收堆外内存(用Unsafe的freeMemory())
    # 如NIO的直接内存DirectByteBuffer
    PhantomReference<M> r = new PhantomReference<>(new M(), QUEUE);
    r.get() // 自己写永远返回null                     

对象分配过程 #

向栈分配                # 不要调参数
    好处
        不必GC
    条件
        线程私有小对象
        无逃逸
        可标量替换(基本类型替换整个对象)
过大,分配到老年代
线程本地分配            # TLAB(Thread Local Allocation Buffer), 不要调参数
    好处
        为了减少线程争用
    条件
        小对象
        占用eden, 默认每个线程占1%
伊甸区
s1,s2
    次数                   # 最大15(对象头上空间决定)
        Parallel Scavenge 15
        CMS 6
        G1 15
    动态年龄
        eden + s1 复制到s2, 超过s2总内存一半时,年龄最大的对象进入老年代
    分配担保
        YGC时survivor区空间不够, 直接进入老年代
GC清除或到老年代

GC分代过程 #

YGC -> s0
YGC, eden + s0 -> s1
    年龄足够 -> old
    s区装不下 -> old
YGC, eden + s1 -> s0
old满了 -> FGC

常见的回收器 #

概念
    Safe Point              # STW时机
    没有无STW的回收器
分代
    Young
        Serial              # 第一个GC
            STW, 单线程串行回收
        Parallel Scavenge
            STW, 并行回收
        ParNew              # Parallel New
            增强PS, 以配合CMS并行回收, CMS到某阶段时PN运行
    Old
        Serial Old
            特点
                适用几十M
                mark-sweep-compact,单线程
        Parallel Old
            特点
                适用几个G
                mark-sweep-compact,多线程
        CMS                 # concurrent mark sweep, 1.4后期引入, JDK11取消
            特点
                适用20G
                多线程并行回收, 并发回收(GC与程序同时运行),降低STW时间(200ms)
            不足            # 问题多,没有版本默认CMS
                浮动垃圾
                碎片多,新对象分配不下时,使用SerialOld
                    设低GC阈值回收浮动垃圾
            清理过程
                初始标记(initial mark)
                    STW, 单线程, 标记根对象
                [预标记]                        # Card Marking, 把Card标为Dirty
                并发标记(concurrent mark)
                    不STW, 多线程, 执行多次
                重新标记(remark)                # 处理并发标记过程中的变化
                    STW, 多线程, 
                并发清理(concurrent sweep)      # 过程中产生的浮动垃圾, 下次回收
                    不STW, 多线程, 
                [整理阶段]
            日志
                [GC(Allocation Failure)[ParNew:6144K->640K(6144K)], 0.02 secs] 6585K->2770K(19840K),0.02 secs][Times:user=0.02 sys=0.00, real=0.02 secs]
                    6144K->640K(6144K): 回收前 回收后 总容量
                    6585K->2770K(19840K): 堆回收前 回收后 总大小  

                [GC (CMS Initial Mark)]
                    [1 CMS-initail-mark]
                [CMS-concurrent-mark-start]
                [CMS-concurrent-preclean-start]
                [GC (CMS Final Remark)]
                    [YG occupancy]              # 清理后年轻代占用及容量
                    [Rescan(parallel)]          # STW下标记存活对象
                    [weak refs processing]      # 弱引用处理
                    [class unloading]           # 卸载用不到的class
                    [scrub symbol table]        # 清理常量池
                    [scrub string table]        # 清理常量池
                    [1 CMS-remark]              # 清理后老年代占用及容量
                [CMS-concurrent-sweep-start]
                [CMS-concurrent-reset-start]

不分代
    G1                      # Garbage First, 1.7引入, 1.8完善, 1.9默认
        特点
            适用上百G
            STW 10ms回收
                容易预测STW时间
                低latency, 但throughput也低
            并发回收, 三色标记
            只逻辑分代, 不物理分代 
                内存分Region, Region组成不同大小块,块在逻辑分代中
                Eden和Old区的内存映射会变化
            动态新老代空间                      # 如果YGC频繁,就Young调大
                不要手工指定, 是G1预测YGC停顿时间的基准, 停顿时间通过参数设置
        概念
            CSet            # Collection Set
                可回收Region集合, 回收时存活的对象被移动
                占堆空间不到1%
            RSet            # Remembered Set
                用于找到谁引用当前对象(对象级别), 记录其他Region的引用
                赋值时有GC写屏障                # 非内存屏障
            CardTable       # YGC定位垃圾,要从Root查所有Old区对象,效率低
                Old区对象引用Young区时, bitmap标DirtyCard。YGC时只扫描DirtyCard
            MixedGC         # 默认45%, 同CMS
                初始标记
                重新标记
                筛选回收    # 筛选Region回收,有用对象复制到其它Region
        日志
            [GC pause (G1 Evacuation Pause) (young) (initial-mark)]         # 复制存活对象, initial-mark在MixedGC时有
            [GC concurrent-root-region-scan-start]                          # 混合回收
            [GC concurrent-mark-start]                                      # 混合回收
            [Full GC (Allocation Failure)]                                  # 无法evacuation时, G1中很严重
    ZGC                     # JDK11, 不分代(SingleGeneration)
        特点
            STW设计10ms, 实际1ms
            适用4T(JDK13 16T)
            内存分块(有大有小)
        概念
            没有RSet, 改进了SATB指针
    Shenandoah              # JDK11
    Epsilon                 # JDK11, debug用
    Zulu
组合
    S + SO
    PS + PO                 # 1.8默认, 10G内存10+秒
    PN + CMS

算法 #

定位
    引用计数(ReferenceCount)
        循环引用问题        # 内部互相引用,没有外部引用
    根可达算法(RootSearching)
        线程栈变量 
        静态变量
        常量池
        JNI指针             # 本地方法用到的对象
并发标记
    三色标记
        白                  # 未被标记
        灰                  # 自身被遍历到,成员变量未被遍历到
        黑                  # 自身、成员变量都被遍历到
        漏标问题 
            两个必要条件 
                黑引用白后,灰对白的引用断开
            算法
                incremental update                      # 增量更新,关注引用的增加, CMS用的
                    增加引用后,标记为灰, 重新标记阶段再扫描
                    缺点是灰色还要重新扫描
                SATB snapshot at the beginning          # 关注引用的删除, G1用的
                    开始时快照, 引用消失时,引用推到堆栈, 下次扫描还扫白色对象
                    优点是只扫描修改过的对象, 看RSet中有没有引用
    颜色指针                # 信息记在指针里
    租户隔离, Session Base GC           # Web专用, 基于session, session断开后删除
    各GC应用
        CMS
            三色标记 + Incremental Update
        G1
            三色标记 + SATB
        ZGC
            颜色指针 + 写屏障
        Shenandoah
            颜色指针 + 读屏障
清除
    标记清除(Mark-Sweep)    # 一遍标记,一遍清理, 适合老年代
        算法简单,戚对象多时效率高
        两遍扫描效率低,容易产生碎片
    拷贝(Copying)           # 存活对象copy到新内存, 旧内存直接清理,适合伊甸区(新生代)
        适用存活对象少的情况
        内存减半
    标记压缩(Mark-Compact)  # 有用的填到前边去空隙去, 适合老年代
        不会碎片,不会内存减半
        扫描两次,还要移动
分代模型
    分代模型                # -Xms -Xmx设置大小
        new/young(新生代)   # MinorGC/YGC, -Xmn设置大小, 默认占比1
            eden(伊甸)      # 默认占比8
            survivor x 2    # 默认每个占比1
        old(老年代)         # MajorGC/FullGC, 1.8默认占比2, 之前是3
            tenured(终身)
        methodArea          # 1.7永久代, 1.8元数据区
    各JVM的分代模型
        Epsilon ZGC Shenandoah不是分代模型
        G1是逻辑分代,物理不分代
        其他都是逻辑分代 + 物理分代

调优(Tuning) #

前提
    从业务场景开始
    无监控(能压测), 不调优
目标
    减少FGC
    确定倾向                        # 吞吐量, 或响应时间
        吞吐量好: PS + PO
        响应时间好: G1 或 PN + CMS  # G1吞吐量少10%
组成部分
    JVM预规划
    优化JVM运行环境(慢、卡顿)
    解决JVM运行时出现的问题(OOM)
步骤
    熟悉业务场景
        响应时间
        吞吐量
    选择回收器组合
    计算内存需求(小的快,大的少gc)
    选CPU
    设定年代大小、升级年龄
    设定日志参数
    观察日志情况

问题分析 #

工具 #

CPU经常100%
    top查进程CPU(top)
    进程中线程CPU(top -Hp)
    导出该线程堆栈(jstack)
    查哪个方法(栈帧)消耗时间(jstack)
内存高
    导出堆内存(jmap)
    分析(jhat jvisualvm mat jprofiler ...)
监控JVM
    jstat jvisualvm jprofiler arthas top ...
    网管: Ansible
流程
    网管报警
    top -Hp 进程号
    jstack 进程号               # 列出所有线程号, 线程状态
        WAITING, 一直等不到, BLOCKED, 拿不到锁
        waiting on <0x0000> (a java.lang.Object)    # 找到目标等待的线程
    jstack -l 16进制线程号      
    jps
    jinfo 进程号                # 列JVM信息
    jstat -gc 进程号 500        # 每500ms打印一次gc信息
    jmap -histo 进程号 | head -20                   # 列所有对象
        有性能消耗,但不很高,可以在线执行
    jmap -dump:format=b, file=x pid                 # 导出转储文件
        内存特别大时,jmap会卡顿
        多个服务器可用,停一个不影响
        设定HeapDumpOnOutOfMemoryError产生堆转储文件                 
        在线定位(中小型公司用不到)
    jhat -J-mx512M x.hprof      # 分析堆dump文件, 有OQL
    arthas                      # 在线定位
        启动
            java -jar arthas-boot.jar
        常用命令                # 没有集成jmap功能
            jvm                 # jinfo
            thread              # jstack
                thread 1
            dashboard           # top
            heapdump            # jmap -dump
            dump
            redefine            # 热替换
                目前只能改method实现,不能改方法名与属性
            jad                 # 反编译类
                看动态代理生成的类
                看第三方类
                看版本
            sc                  # search class, 显示class信息
            watch               # watch method
    MAT                         # 分析dump文件
    jprofiler
    jconsole                    # 需要JMX
        JMX会消耗性能生产服务器不开
        JMX图形界面只用于压测
    jvisualVM                   # 需要JMX, 可分析dump文件

内存 #

现象
    OOM崩溃
    CPU飙高, 不断FGC
线程池不当运用
加内存反而卡顿
    GC, 应该用CMS或G1替换 PS+PO
JIRA不停FGC, 没定位出来
    扩内存到50G, GC换G1, 重启
tomcat server.max-http-header-size过大
    默认4096, 每个请求都分配
lambda表达式导致方法区溢出
    java.lang.OutofMemoryError: Compressed class space
disruptor不释放缓存
使用Unsafe分配内存, 直接内存溢出
-Xss设定小, 栈溢出
重写finalize()引发GC
    finalize()耗时长, GC时回收不过来,不停GC
内存不到10%,频繁FGC
    有人显式调用System.gc()                         # 不定时调,但会频繁调
大量线程, native thread OOM
    减少堆空间,留更多系统内存产生native thread
G1产生FGC
    降低MixedGC触发的阈值       # 默认45%
    扩内存
    提高CPU                     # 回收快

HotSpot参数 #

辅助
    -XX:+PrintCommandLineFlags -version             # 打印启动参数, -version是随便一个命令
    -XX:+PrintFlagsFinal -version                   # 打印所有XX参数
    -XX:+PrintFlagsInitial      # 打印默认参数
    -XX:+PrintVMOptions         # 显示VM启动参数
    -                           # 标准参数
    -X                          # 显示非标参数
    -XX                         # 显示不稳定参数
内存
    -XX:+HeapDumpOnOutOfMemoryError                 # OOM时产生堆转储文件 
    -Xms40M                     # 堆起始大小
    -Xmx60M                     # 堆最大大小, 最好和Xms一样以免堆弹大弹小
    -Xmn                        # 年经代
    -Xss                        # 栈空间
    -XX:InitialHeapSize         # 起始堆大小,自动算
    -XX:MaxHeapSize             # 堆最大大小,自动算
内存模型
    -XX:-DoEscapeAnalysis       # 去逃逸分析
    -XX:-EliminateAllocations   # 去标量替换
    -XX:-UseTLAB                # 去tlab
    -XX:TLABSize                # 设置TLAB大小
    -XX:+PrintTLAB
    -XX:MaxTenuringThreshold    # 进老年代(升代)回收次数, 最大值15, CMS默认6,其它默认15
对象和类
    -XX:+UseCompressedClassPointers                 # class指针压缩
        开启时4字节,不开启时8字节
    -XX:+UseCompressedOops                          # 引用类型指针压缩, Ordinary Object Pointers
        开启为4字节,不开启时8字节
    -verbose:class              # 类加载详细过程
    -XX:PreBlockSpin            # 锁自旋次数
编译
    -Xmixed                     # 混合模式
    -Xint                       # 解释模式
    -Xcomp                      # 编译模式
    -XX:CompileThreshold = 10000                    # 检测热点代码次数
GC
    -XX:+PrintGC                # 打印GC信息
    PrintGCDetails              # 打印GC更详细
    PrintGCTimeStamps           # 打印GC时间
    PrintGCCauses               # GC原因
    PrintHeapAtGC
    PrintGCApplicationConcurrentTime                # GC应用程序时间
    PrintCApplicationStoppedTime                    # 打印STW时长
    -XX:+PrintReferenceGC       # 打印回收多少种引用类型
    -XX:+UseConcMarkSweepGC     # 用CMS
    -XX:+DisableExplictGC       # System.gc()不管用

    Parallel常用
        -XX:SurvivorRatio           # 新生代Eden区和Surivor区的比例
        -XX:PreTenureSizeThreshold  # 大对象到底多大
        -XX:+ParallelGCThreads      # 并发线程数, 默认是CPU数
        -XX:+UseAdaptiveSizePolicy  # 自动调所有区比例
    CMS常用
        -XX:ParallelCMSThreads      # 并发线程数,默认是CPU数一半
        -XX:CMSInitiatingOccupancyFraction 92%          # 老年代占多少时触发GC, 1.8 92%, 之前68%
            设小一点,清除浮动垃圾
            过大时,栈分配不下,Promotion Failure,触发FGC
        -XX:+UseCMSCompactAtFullCollection              # GC时压缩,避免碎片片
        -XX:CMSFullGCsBeforeCompaction                  # 多少次GC后压缩
        -XX:+CMSClassUnloadingEnabled                   # 回收方法区
        -XX:CMSInitiatingPermOccupancyFraction          # 到什么比例时进行Perm回收, 1.8之前
        GCTimeRatio                                     # GC占程序运行时间的百分比
        -XX:MaxGCPauseMillis                            # GC停顿时间, CMS会减少年轻代大小
    G1
        -XX:MaxGCPauseMillis                            # STW时间, 区别CMS, G1会调整Young区的块数
        GCTimeRatio
        -XX:GCPauseIntervalMillis                       # STW之间间隔时间
        -XX:+G1HeapRegionSize                           # Region大小, 1 2 4 8 16 32, 逐渐增大, GC间隔更长, 每次GC时间更长
            ZGC是动态调整的
        G1NewSizePercent                                # 新生代最小比例, 默认5%
        G1MaxNewSizePercent                             # 新生代最大比例,默认60%
        ConcGCThreads                                   # GC线程数
        InitiatingHeapOccupancyPercent                  # 启动GC的堆空间占用比例

JMX监控
    -Djava.rmi.server.hostname=192.168.1.1
    -Dcom.sun.management.jmxremote 
    -Dcom.sun.management.jmxremote.port=11111 
    -Dcom.sun.management.jmxremote.authenticate=false 
    -Dcom.sun.management.jmxremote.ssl=false
调优                            # 参数越来越少
    JVM参数800个
    CMS参数300个
    G1参数100个
    ZGC更少
    Zing1个
GC组合参数
    -XX:+UseSerialGC
        S + SO
    -XX:+UseParNewGC                # 已废弃
        PN + SO
    -XX:+UseConc(urrent)MarkSweepGC
        PN + CMS + SO
    -XX:+UseParallelGC               # 1.8默认
        PS + PO
    -XX:+UseParallelOldGC
        PS + PO
    -XX:+UseG1GC
        G1
日志参数
    -Xloggc:/logs/xx-xx-%t.log
    -XX:+UseGCLogFileRotation           # 5个满了,覆盖第一个
    -XX:NumberOfGCLogFiles=5
    -XX:GCLogFileSize=1024M
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -XX:+PrintGCCause

HotSpot日志 #

GC                          # PrintGCDetails
    [GC
        GC表示YGC, Full GC是FGC
    (Allocation Failure)
        原因
    [DefNew:4544k->259k(6144k), 0.0873 secs]
        DefNew表示年轻代, 回收前后的大小, 6144是年轻代总大小,回收时间
    4544k->4356k(19840k), 0.0812 secs]
        堆的前后大小, 19840是堆总空间, 回收时间
    [Times: user=0.01 sys=0.00, real=0.01 secs]
        linux time命令,用户态时间,内核态时间,总时间
异常退出dump堆
    def new generation total 6144k, used 5504k [0x00, 0x00, 0x00]
        新生代总共多少,用了多少。内存起始地址,使用空间结束地址,整体空间结束地址
        total = eden + 1个survivor
    eden space 5504k, 100% used []
        eden
    from space 640k, 0% used []
        s0
    to space 640, 0% used []
        s1
    tenured generation total 13696k, used 13312k []
        old
    the space 13696k, 97% used []
        old
    Metaspace used 2538k, capacity 4486k, committed 4864k, reserved 1056768k
        used真正使用的大小
        capacity目前指定的容量 
        committed 表示预先占用的大小
        reserved表示共保留的大小
    class space used 275k, capacity 386k, committed 512k, reserved 1048576k
        metaspace中存class的部分