JVM-从熟悉到精通

news2024/7/6 19:05:28

JVM

机器语言

一个指令由操作码和操作数组成

方法调用等于一个压栈的过程

  • 栈有 BP寄存器 和 SP寄存器来占用空间
    • BP -> Base Point 栈基址(栈底)
    • SP -> Stack Point 栈顶

字节序用于规定数据在内存单元如何存放,二进制位的高位和低位映射到地址内存的高位和地位

  • 高地址放在低地址的前面叫大端序
  • 低地址放在高地址的前面叫小端序
    请添加图片描述
//翻译成汇编语言
//b 8位   w 16位  l 32位  q 64位
//sub $4, %esp;
//movl $1, -4(%ebp);
int i = 1;

字节码文件

Magic Number:CAFE BABE

Minor Number:小版本号

Major Number:大版本号

Constant Pool Count:常量池长度

Constant Pool:常量池字节码 -> 常量池长度-1个常量,每个常量为1个字节的标志位+2个字节的实际值

Access Flag:修饰符

This_class 存储常量池的引用

super_class 存储常量池的引用

Interface Count 接口数量

Interfaces 接口,常量池的引用

Fields Count 属性数量

Fields 属性 常量池的引用

Methods Count 方法数量

Methods 方法 常量池引用

Attribute Count

Attribute :code Java的汇编代码

请添加图片描述

类加载

Loading

  • 类加载主要是将.class文件通过二进制字节流的方式读入JVM中。加载阶段JVM需要完成三件事

    • 通过classloader将.class文件读入内存
    • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
    • 在内存中生成一个该类的java.lang.Class对象,作为方法区这个类各种数据的访问入口
  • 类加载器

    • Bootstrap(rt.jar、charset.jar等核心类 加载器由c++实现)
    • Extension(lib/ext/*.jar)
    • Application(classpath/ *.jar… *.class)
    • 自定义类加载器,只需要重写findClass方法即可
  • 双亲委派

    • 优先使用上层加载器进行加载,捕获异常再尝试使用下层加载器

    • 优点:

      • 有效避免了某些恶意类的加载,安全问题是主要原因
      • 每个类只被加载一次,避免了重复加载,提高效率

      请添加图片描述

    • 线程的默认加载器是applicationClassLoader

    请添加图片描述

  • LazyLoading 五种情况

    • –new getstatic putstatic invokestatic指令,访问final变量除外(final变量不需要加载就能读取到)
    • –java.lang.reflect对类进行反射调用时
    • –初始化子类的时候,父类首先初始化
    • –虚拟机启动时,被执行的主类必须初始化
    • –动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化

Linking

  • Verification 验证
    • 验证文件是否符合JVM规定
  • Preparation 准备
    • 将静态变量在方法区分配内存,并设置默认值
  • Resolution 解析
    • 将类、方法、属性等符号引用解析为直接引用(将符号引用转换为指向内存地址的指针)
    • 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

Initializing

  • 调用类初始化代码
    • 父类初始化
    • static变量赋初始值/static块赋初始值

小总结:

  1. load - 默认值 - 初始值
  2. new - 申请内存 - 默认值 - 初始值

是ldc、iconst、lconst、fconst、dconst还是ldc指令,是根据赋值的大小来进行判定,在编译时进行处理

// 10在哪儿存?
// 在操作数栈中,字节码的指令中	bipush 10
static int a = 10;

// 10在哪儿存?
// 在静态常量池,因为java的指令中没有bfipush,只能复用 ldc #2 + putstatic 
// float double 对象等初始化放到常量池中,运行时通过ldc指令,将他们的地址放入操作数栈来操作
static float a = 10.0;

Java执行引擎

混合模式

Java默认使用解释器+编译器组合来执行代码

起始阶段采用解释执行

热点代码会进行编译成本地文件执行

检测热点代码:

  • 多次被调用的方法(方法计数器:检测方法执行频率)
  • 多次被调用的循环(循环计数器:检测循环执行频率)
  • 进行编译

JIT即时编译器

JIT内部包含:中间代码生成器、代码优化器、目标代码生成器、探测分析器。

JIT会将多个字节码文件生成的指令进行打包、优化、编译成本地代码然后放到方法区中,当执行代码时直接执行机器代码or汇编代码即可,不再需要一行行进行解释执行,增加代码执行效率。

可以通过配置参数来指定Java引擎执行模式

  • -Xmixed 默认为混合模式 开始解释执行热点代码编译执行
  • -Xint 使用解释执行,启动速度很快,执行稍慢
  • -Xcomp 使用纯编译模式,执行很快,启动很慢
    • 热点代码检测阈值 -XX:CompileThreshold = 10000

JMM

硬件层数据一致性

现代计算机存储器

请添加图片描述

MESI

协议很多

intel 用MESI(CPU给每个缓存行使用4种状态标记)

  • Modified 当前缓存行在当前cpu被修改过
  • Exclusive 当前缓存行只在当前cpu中被缓存,为当前cpu独享
  • Shared 当前缓存行被多个cpu共享,且在多个cpu中当前缓存行数据一致
  • Invalid 当前cpu持有的当前缓存行无效。(被其他cpu修改过)

参考文档:https://www.cnblogs.com/z00377750/p/9180644.html

现代CPU的数据一致性实现 = 缓存锁(MESI …) + 总线锁

缓存行

读取缓存以cache line为基本单位,目前64bytes

位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的伪共享问题

伪共享问题:JUC/c_028_FalseSharing

使用缓存行的对齐能够提高效率(填充到64bytes保证不会与其他线程发生缓存行伪共享)

请添加图片描述

乱序问题

CPU为了提高指令执行效率,会在一条指令执行过程中(比如去内存读数据(慢100倍)),去同时执行另一条指令,前提是,两条指令没有依赖关系

写操作也可以进行合并(WCBuffer 合并写 是另一种形式的缓存,更新后数据直接发送到L2级别缓存,一般只有4个字节)

参考文档:https://www.cnblogs.com/liushaodong/p/4777308.html

参考代码:JUC/029_WriteCombining

Java中的乱序执行

原始参考:https://preshing.com/20120515/memory-reordering-caught-in-the-act/

public class T04_Disorder {
    private static int x = 0, y = 0;
    private static int a = 0, b =0;

    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        for(;;) {
            i++;
            x = 0; y = 0;
            a = 0; b = 0;
            Thread one = new Thread(new Runnable() {
                public void run() {
                    //由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.
                    //shortWait(100000);
                    a = 1;
                    x = b;
                }
            });

            Thread other = new Thread(new Runnable() {
                public void run() {
                    b = 1;
                    y = a;
                }
            });
            one.start();other.start();
            one.join();other.join();
            String result = "第" + i + "次 (" + x + "," + y + ")";
            if(x == 0 && y == 0) {
              // 理论上如果不发生指令重排是不可能出现x,y都为0的情况
                System.err.println(result);
                break;
            } else {
                //System.out.println(result);
            }
        }
    }


    public static void shortWait(long interval){
        long start = System.nanoTime();
        long end;
        do{
            end = System.nanoTime();
        }while(start + interval >= end);
    }
}

如何保证特定情况下不乱序

硬件内存屏障 X86

在内存屏障指令前后的读or写不可乱序执行

sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
lfence:load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
mfence:modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序

JVM级别如何规范(JSR133)

LoadLoad屏障:
对于这样的语句Load1; LoadLoad; Load2,

在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:

对于这样的语句Store1; StoreStore; Store2,

在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

LoadStore屏障:

对于这样的语句Load1; LoadStore; Store2,

在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障:

对于这样的语句Store1; StoreLoad; Load2,

在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

volatile的实现细节

  1. 字节码层面
    ACC_VOLATILE

  2. JVM层面
    volatile内存区的读写 都加屏障

    StoreStoreBarrier

    volatile 写操作

    StoreLoadBarrier

    LoadLoadBarrier

    volatile 读操作

    LoadStoreBarrier

  3. OS和硬件层面
    参考文章:https://blog.csdn.net/qq_26222859/article/details/52235930
    hsdis - HotSpot Dis Assembler
    windows lock 指令实现 | MESI实现

synchronized实现细节

  1. 字节码层面
    ACC_SYNCHRONIZED
    monitorenter + monitorexit
  2. JVM层面
    C C++ 调用了操作系统提供的同步机制
  3. OS和硬件层面
    X86 : lock cmpxchg / xxx
    https😕/blog.csdn.net/21aspnet/article/details/88571740

Happens Before原则

Java语言的规范:如果两个操作之间具有happen-before关系,那么前一个操作的结果就会对后面的一个操作可 见。是Java内存模型中定义的两个操作之间的偏序关系。

常用于锁、volatile、传递性、线程启动、线程终止、线程中断

对象内存布局

对象创建过程

  • load class
    • loading
    • linking verification preparation resolution
    • intiallizing
  • 申请内存空间
  • 成员变量赋默认值
  • 调用构造方法
    • 成员变量赋初始值
    • 执行构造语句(先执行super())

对象结构

普通对象

  • 对象头 markword 8字节

    • 默认开启压缩指针(-XX:+UseCompressedClassPointers) 64位下为4字节 不开启为8字节
  • 类指针(指向对象对应的class对象)classpointer

  • 实例数据 instance

    • 默认开启压缩普通对象指针(-XX:+UseCompressedOops )64位下为4字节 不开启为8字节
    • Oops Ordinary Object Pointers 普通对象指针与class对象是不同的压缩对象
  • 对齐位数 padding(对齐后整个对象为8个字节的倍数)

数组对象

  • markword 同普通对象

  • classpointer 同普通对象

  • 数组长度 4字节

  • 数组数据

  • 对齐位数 同普通对象

// markword8字节 classpointer4字节(关闭压缩指针则为8字节) padding后 为16字节
System.out.println(ObjectSizeAgent.sizeOf(new Object()));
// markword8字节 classpointer4字节(关闭压缩指针则为8字节)数组长度4字节 padding后为16字节or24字节
System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));
// markword8字节 classpointer4字节(关闭压缩指针则为8字节)
// 对象引用默认为4字节 padding后为32字节or40字节(压缩指针)
System.out.println(ObjectSizeAgent.sizeOf(new P()));

private static class P {
  //8 _markword
  //4 _class pointer
  int id;         //4
  String name;    //4
  int age;        //4

  byte b1;        //1
  byte b2;        //1

  Object o;       //4
  byte b3;        //1

}

markword具体内容

synchronized锁升级->markword变化

  1. 当一个对象刚开始new出来时,该对象是无锁状态。此时偏向锁位为0,锁标志位01
    • 如果调用了对象的hashcode方法且该方法未被重写会System.identityHashCode获取当前hashcode并写入markword
  2. 如果有线程上锁
    • 将markword中的线程Id改为当前线程Id cas操作
  3. 如果有线程竞争
    • 撤销偏向锁,升级为轻量级锁
    • 线程在自己的线程栈中生成lock_record,通过CAS操作让markword指向当前线程的lock_record
  4. 如果竞争加剧
    • 有线程超过10次自旋 -XX:PreBlockSpin,或者自旋线程数量超过cpu核数的一半
    • 升级为重量级锁,向操作系统升级资源,等待操作系统的线程调度然后映射到用户空间,此时markword指向对象监视器moniter

请添加图片描述

请添加图片描述

运行时数据区

方法区+jvm堆是线程共享的 即非线程安全

虚拟机栈、本地方法栈、程序计数器是线程独享的 即线程安全

直接内存 Direct Memory

jvm直接访问内核空间的内存

nio的zero copy即使用了这块儿空间

方法区 Method Area

运行时常量池 runtime constant pool(class字节码中的constant pool)

类信息、常量、静态变量、即时编译器编译后的代码。类的模板

Jdk8以后存储于metaspace,即在jvm堆外的进程堆中(本地内存中)

字符串常量池1.7以前是位于PermSpace 而1.8以后位于堆

1.7以前使用PermSapce实现 1.8以后位于MetaSpace,PermSize FGC不回收

JVM8使用metaSpace代替PermGen的好处

  1. 字符串存在永久代,容易出现性能问题及内存溢出
  2. 类及方法信息比较难以确定大小,所以难以设置永久代大小参数。太小容易永久代溢出,太大容易老年代溢出
  3. 为GC带来了不必要的复杂性且回收效率偏低
  4. 元空间特点(放在直接内存中而不是JVM的堆中)
    • 每个加载器有专门的存储空间
    • 不会单独回收某个类
    • 元空间里对象的位置是固定的
    • 如果发现某个加载器不再存活会将相关的空间整个回收

堆 Heap

存放对象实例

虚拟机栈 JVM Stack

用于执行java代码,每个线程对应一个栈

栈帧Frame

每个方法对应一个栈帧

局部变量表 Local Variables Table

当前方法的局部变量,包括方法参数及方法内部使用的变量。当方法为非static时,局部变量表中会有this(下标为0)

操作数栈 Oparand Stacks

栈,指令会操作压栈及弹出栈赋值给局部变量表中的数据

动态链接 Dynamic Linking

指去class文件的constant pool里面寻找对应的符号引用,并将符号引用解析为直接引用的过程

返回地址 Return address

a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方以及b方法返回后从哪个位置继续执行

本地方法栈 Native Method Stack

存储调用c语言的方法(Native方法)

程序计数器 Program Counter

记录当前线程执行的指令位置,类似一个行号

存放指令位置

虚拟机的运行,类似于这样的循环:

while( not end ) {

​ 取PC中的位置,找到对应位置的指令;

​ 执行该指令;

​ PC ++;

}

GC

垃圾:没有引用指向的对象即为垃圾

定位垃圾:

  • 引用计数 ReferenceCount
  • 根可达分析 Rooting Search

GC roots

  • 线程栈变量 栈帧中所有对象
  • 静态变量
  • 运行时常量池
  • JNI指针

常用垃圾回收算法

标记清除

适合存活对象比较多(需要清除对象比较少)的情况

需要两次扫描,执行效率偏低

容易产生内存碎片

标记拷贝

适合存活对象比较少的场景(移动存活的对象),只扫描一次

移动复制对象,需要调整对象引用

空间浪费

标记压缩

不会产生内存碎片,方便对象分配;不会产生内存减半

移动复制对象,需要调整对象引用

需要扫描两次

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

除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型

G1是逻辑分代,物理不分代

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

老年代

新生代

  • 分为 Eden Survivor1 Survivor2
  • 大小默认比例为 8:1:1

老年代对象存活率高 所以适合标记清除or标记压缩算法

新生代对象存活率低 所以适合标记复制算法

新生代默认占用堆内存1/3空间,老年代默认占用堆空间2/3

对象默认分配位置

栈上分配
  • 线程私有小对象

  • 无逃逸

  • 支持标量替换

线程本地分配TLAB Thread Local Allocation Buffer

每个线程在eden申请1%大小的小空间做线程私有,优先往这里分配对象

  • 占用eden,默认1%
  • 多线程不用竞争eden就可以申请空间,提高效率
  • 小对象
老年代

大对象

对象进入老年代时点

  1. 超过XX:MaxTenuringThreshold指定的阈值

    • 默认
      • Parallel Scavenge 15
      • CMS 6
      • G1 15
  2. 动态年龄

    • 年龄从小往大累加,当加入某个年龄的对象超过survivor空间的50%时,从该年龄往上都放入老年代
    • 例:年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升。

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

  3. 分配担保

    • YGC期间 survivor区空间不够了 空间担保直接进入老年代
    • 不同垃圾回收器具体实现不尽相同
      • Serial+Serial Old场景 新生代内存不够直接将新生代对象放入老年代,新对象放入新生代。
      • Parallel Scavenge+Serial Old场景 先判断新对象是否超过新生代一半,如果超过将新对象直接放入老年代,如果不超过启用担保机制同上。

    参考:https://cloud.tencent.com/developer/article/1082730

  4. 大对象直接放入老年代

对象内存分配顺序

请添加图片描述

GC前置知识

  • 并行:多个垃圾回收线程同时执行,此时用户线程停顿。Stop The World

  • 并发:垃圾回收线程和用户线程同时执行

  • 安全点:方法调用、循环跳转、异常跳转;所有线程都处于安全点才可以做垃圾回收

    • 抢占式中断:中断所有线程,如果有线程不处于安全点则恢复,等待跑到安全点
    • 主动式中断:设置一个中断标志,各个线程处于安全点后主动轮询,标志为真则中断挂起
  • 安全区域:线程处于sleep or block状态下,标记进入safe region,此时也是可以做垃圾回收的,如果线程即将离开safe region,会检查时候完成gc,如果未完成需要等待完成才可以离开safe regioin

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

  • 三色标记

  • 颜色指针

常用垃圾回收器

请添加图片描述

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

  2. Serial 年轻代 串行回收

  3. PS 年轻代 并行回收

    添加了新生代自适应调整,可以有JVM自己控制Eden和Survivor比例及新生代所占用堆大小;吞吐量优先,可以设置最大垃圾回收停顿时间、可以直接设置吞吐量大小。(吞吐量:用户代码运行时间/用户代码运行时间+垃圾回收时间)

  4. ParNew 年轻代 配合CMS的并行回收

  5. SerialOld

  6. ParallelOld

    注重高吞吐量以及CPU敏感的场合,都可以优先考虑PS+PO的组合

  7. ConcurrentMarkSweep 老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms)
    CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定
    CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收
    想象一下:
    PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW)
    几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC
    算法:三色标记 + Incremental Update

  8. G1(10ms)
    算法:三色标记 + SATB

  9. ZGC (1ms) PK C++
    算法:ColoredPointers + LoadBarrier

  10. Shenandoah
    算法:ColoredPointers + WriteBarrier

  11. Eplison

  12. PS 和 PN区别的延伸阅读:
    ▪https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-3D0BB91E-9BFF-4EBB-B523-14493A860E73

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

    1. Serial 几十兆
    2. PS 上百兆 - 几个G
    3. CMS - 20G
    4. G1 - 上百G
    5. ZGC - 4T - 16T(JDK13)

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

CMS

  • 获取最短回收停顿时间为目标的收集器,并发收集、低延迟
  • 注重服务响应速度,希望系统停顿时间短的场景可以使用CMS回收。如web程序、B/S服务等
  • 初始标记、并发标记、重新标记、并发清除、重置线程
  • 缺点
    • 对CPU资源敏感
    • 无法处理浮动垃圾,可能由于Concurrent Model Failure失败,导致触发一次FullGC
      • 垃圾回收线程与用户线程并发执行,会导致本次用户线程产生的内存垃圾无法清除,下一次垃圾回收才会清除
    • 因为是标记清除算法,内存碎片会导致大对象无法分配,导致一次FullGC
    • 由于并发执行环节多,会占用更多的内存空间,降低吞吐量

请添加图片描述

G1(Garbage First)

G1可以根据用户设置的暂停时间目标自动调整年轻 代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大

筛选回收

  • 将内存划分为多个region块,垃圾回收会优先回收垃圾更多的块。

回收算法是标记清除+标记整理

  • Region内部是标记清除
  • Region间是标记整理

可预测的停顿时间

  • G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒

核心知识点

  • CSet Collection Set

    一组可以被回收的分区的集合,记录有哪些Region可以被回收

    在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden、Suvivor或者老年代。

    Cset会占用堆内存1%大小以内的空间

  • RSet Remembered Set

    记录了其他Region中的对象对于本Region的引用,每个Region都有自己的RSet

    使得垃圾回收器不需要扫描整个堆就知道哪些哪些Region引用了当前Region,直接扫描RSet即可、

    判断当前Region是否可回收时,效率更高

    由于RSet的存在,在G1垃圾回收器下,每次给对象赋引用需要一些额外的操作(记录引用信息),这个在GC中被称为写屏障,与内存屏障无关

  • 新生代老年代比例 5%-60%

    不需要手动指定,让G1基于时间停顿自己去优化

  • 大对象 humongous object

    默认超过单个Region50%大小即认为是大对象,大对象可以跨Region

  • 如果G1发生FullGC且时间较长怎么办(G1 FullGC 同CMS 在1.8是串行的)

    • 提高内存和CPU
    • 减小触发Mixed GC的阈值 更早触发Mixed GC
  • Mixed GC

    • 流程上与CMS相同
      • 初始标记
      • 并发标记
      • 最终标记
      • 筛选回收(并行 优先回收垃圾更多的Region)
    • 默认值为堆内存占用超过45%则触发
  • 三色标记法

    • 白色:未被标记的对象
    • 灰色:自身被标记,成员变量未被标记
    • 黑色:自身与成员变量均已标记完成
    • 漏标
      • 已被标记为黑色的对象引用指向了白色且与此同时原来为灰色且指向白色对象的引用被删除,此时会发生漏标。
      • 避免漏标的方式or算法
        • 追踪A指向D的增加–> intermental update --增量更新,关注引用的增加,将黑色对象重新标记为灰色对象,下次重新扫描属性 CMS并发标记期间使用的算法
        • 追踪B指向D的删除—> STAB snapshot at the beginning - 关注引用的删除,B->D的引用删除时,把这个引用推到GC的堆栈,保证D还能够被扫描到 G1并发标记期间使用的算法
          • STAB 处理的效率更高,不需要再重新扫描
          • 扫描GC的堆栈时,依次查看引用的对象所在的region中是否含有指向这些引用的region(RSet)即可判断是否可以回收 —> STAB与RSet配合使用提高标记效率

    请添加图片描述

常见垃圾回收器组合参数设定:(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
  • -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的命令行参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

  • HotSpot参数分类

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

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

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

    java -version

    java -X

    试验用程序:

    import java.util.List;
    import java.util.LinkedList;
    
    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. 区分概念:内存泄漏memory leak,内存溢出out of memory
    2. java -XX:+PrintCommandLineFlags HelloGC
    3. java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
      PrintGCDetails PrintGCTimeStamps PrintGCCauses
    4. java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
    5. java -XX:+PrintFlagsInitial 默认参数值
    6. java -XX:+PrintFlagsFinal 最终参数值
    7. java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数
    8. java -XX:+PrintFlagsFinal -version |grep GC

PS GC日志详解

每种垃圾回收器的日志格式是不同的!

PS日志格式

请添加图片描述

heap dump部分:

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

请添加图片描述

total = eden + 1个survivor

什么是调优?

  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. 测试代码:

    package com.mashibing.jvm.gc;
    
    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;
    
    /**
     * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
     */
    
    public class T15_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

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

  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,查找有多少对象产生

  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. 使用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. 找到代码的问题

jconsole远程连接

  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加入进去

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

    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

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保持老年代足够的空间

    -> 达到阈值会产生一次FGC,降低阈值代表更频繁的FGC防止退化为Serial Old

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):整个堆大小

[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

G1日志详解

[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]

案例汇总

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

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

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

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

  4. tomcat http-header-size过大问题(Hector)

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

    "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. 比较一下这两段程序的异同,分析哪一个是更优的写法:

    Object o = null;
    for(int i=0; i<100; i++) {
        o = new Object();
        //业务处理
    }
    
    for(int i=0; i<100; i++) {
        Object o = new Object();
    }
    
  9. 重写finalize引发频繁GC
    小米云,HBase同步系统,系统通过nginx访问超时报警,最后排查,C++程序员重写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%

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:+ UseG1GC。(值得注意的是JDK9中CMS已经被Deprecated,不可使用!移除 该选项)
  2. 如果使用的是jdk8,并且堆内存达到了16G,那么推荐使用G1收集器,来控制每次垃圾收集 的时间。
  3. 如果响应时间是高优先级,或使用的堆非常大,请使用-XX:UseZGC选择完全并发的收集器。(值得注意的是JDK11开始可以启动ZGC,但是此时ZGC具有实验性质,在JDK15中 [202009发布]才取消实验性质的标签,可以直接显示启用,但是JDK15默认GC仍然是G1)

总体原则:减少STOP THE WORD时间,使用并发收集器(比如CMS+ParNew,G1)来减少暂停时间, 加快响应时间,并使用并行收集器来增加多处理器硬件上的总体吞吐量

纤程

程序 进程 线程 纤程

纤程与线程对比

  • 线程切换调度由cpu决定,通过进程的栈来实现;线程切换需要用户态内核态的切换
  • 纤程切换调度由线程决定,通过线程的栈来实现调度;只发生在用户态

纯计算场景下纤程性能会更好,因为jdk原生不支持,所以只能通过agent做代理给Fiber做一个栈的管理

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/392146.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机组成原理|第二章(笔记)

目录第二章 计算机的发展及应用2.1 计算机的发展史2.1.1 计算机的生产和发展2.1.2 微型计算机的出现和发展2.1.3 软件技术的兴起与发展2.2 计算机的应用2.3 计算机的展望上篇&#xff1a;第一章&#xff1a;计算机系统概论 第二章 计算机的发展及应用 2.1 计算机的发展史 2.1.…

基于半车悬架的轴距预瞄与轴间预瞄仿真对比

目录 前言 1. 半车悬架模型 2.轴距预瞄(单点预瞄)和轴间预瞄(两点预瞄)原理与仿真分析 2.1轴距预瞄(单点预瞄) 2.1.1预瞄原理 2.2.轴间预瞄(两点预瞄) 2.2.1预瞄原理 2.3仿真分析 3.总结 前言 对于悬架而言&#xff0c;四个车轮实际的输入信息是受到前后延时以及左右相…

SpringCloud:Feign的使用及配置

目录 Feign的使用及配置 1、Feign替代RestTemplate 2、使用Fegin步骤 3、自定义配置 4、Feign使用优化 5、Feign的最佳实践方式 Feign的使用及配置 1、Feign替代RestTemplate RestTemplate方式远程调用的问题 问题&#xff1a; 1、代码可读性差&#xff0c;编程体验不同…

HTML基本概述

文章目录网站和网页浏览器的作用HTML标签元素注释乱码问题web系统是以网站形式呈现的&#xff0c;而前端是以网页形式呈现的。 网站和网页 网站&#xff08;web site&#xff09;&#xff1a;互联网上用于展示特定内容的相关网页的集合。也就是说&#xff0c;一个网站包含多个…

【预告】ORACLE Primavera P6 v22.12 虚拟机发布

引言 离ORACLE Primavera P6 EPPM最新系统 v22.12已过去了3个多月&#xff0c;应盆友需要&#xff0c;也为方便大家体验&#xff0c;我近日将构建最新的P6的虚拟环境&#xff0c;届时将分享给大家&#xff0c;最终可通过VMWare vsphere (esxi) / workstation 或Oracle virtua…

SQL 窗口函数详解

SQL窗口函数详解 窗口函数的主要作用是对数据进行分组排序、求和、求平均值、计数等。 一、窗口函数的基本语法 <分析函数> OVER ([PARTITION BY <列清单>] ORDER BY <排序用列清单> [ROWS BETWEEN 开始位置 AND 结束位置])理解窗口函数的基本语法&#xff…

opencv校正图像

目录1、前言2、例程2.1、代码2.2、效果口罩说明书网页3、按步骤分析转灰度图降噪 Canny边缘检测膨胀&#xff08;可视具体情况省略&#xff09;轮廓检索选取角度1、前言 我们用相机拍照时&#xff0c;会因为角度问题造成拍歪&#xff0c;会影响图像的识别&#xff0c;这时就需…

【PyTorch】教程:torch.nn.Hardtanh

torch.nn.Hardtanh 原型 CLASS torch.nn.Hardtanh(min_val- 1.0, max_val1.0, inplaceFalse, min_valueNone, max_valueNone) 参数 min_val ([float]) – 线性区域的最小值&#xff0c;默认为 -1max_val ([float]) – 线性区域的最大值&#xff0c;默认为 1inplace ([bool]) …

ABP(ASP.NET Boilerplate)配置整合使用Mysql数据库

ABP默认是支持sqlserver数据库的&#xff0c;但是这并不影响使用其他数据库&#xff0c;稍微配置一下就行了&#xff01;很简单——————————— 一、 卸载原来存在Sql Server的依赖包 在程序包管理控制台输入&#xff0c;选择EntityFrameworkCore 然后执行删除包的命令…

基于intel x86+fpga智能驾驶舱和高级驾驶辅助系统硬件设计(二)

系统功能架构及各模块功能介绍 智能驾驶舱和高级驾驶辅助系统是一个车载智能终端嵌入式平台&#xff0c;系统是一个能够运行 虚拟化操作系统的软件和硬件的综合体。本文的车载主机包括硬件主控处理器、电源管理芯 片、存储设备、输入输出控制器、数字仪表系统系统、后座娱乐系统…

抖音怎么合理安排直播内容|辽宁千圣文化

抖音主播们可以利用直播的方式达到带货的底模&#xff0c;那么做主播的话&#xff0c;就要利用好抖音主播中心&#xff0c;很多抖音用户却表示找不到抖音主播中心&#xff0c;那么怎么去看呢&#xff1f;跟着辽宁千圣文化小编来一起看看吧&#xff01;如何成为一名合格的主播&a…

【操作系统原理实验】调度算法模拟实现

选择一种高级语言如C/C等&#xff0c;模拟实现调度算法完成资源分配与回收的过程。2) 自定义PCB等核心数据结构&#xff1b;3) 利用列表、队列等容器类或者其他数据结构管理PCB,完成相应调度算法的模拟&#xff1b;4) 实现外围一些命令如创建进程、查看进程、关闭进程等&#x…

Spacedesk软件推荐,让你的平板也变成电脑的副屏

我的设备&#xff1a; 电脑:戴尔G15 5511、i7-11800H、Windows 11、RTX3060 平板&#xff1a;荣耀V6、麒麟985、安卓10、分辨率2000*1200&#xff08;手机也行&#xff0c;我用的平板&#xff09; 实际使用&#xff1a; 先给放一张实际使用的照片 可以让平板变成电脑的副屏…

28 位委员出席,龙蜥社区第 15 次运营委员会会议顺利召开

2 月 24 日&#xff0c;龙蜥社区在海光召开了第 15 次运营委员会会议&#xff0c;本次会议由统信软件运营委员会委员崔开主持。来自 Arm、阿里云、飞腾、红旗软件、海光、Intel、龙芯、联通软研院、浪潮信息、普华基础软件、统信软件、万里红、移动、中科方德等理事单位的 28 位…

echarts--提示框显示不全问题记录

最近接手一个同事之前做的网页&#xff0c;发现里面使用echarts来绘制各类图表&#xff1b;有2个问题一个是提示框显示不全&#xff0c;另一个就是绘制总是有部分数据显示不全。后者就是div宽度问题。。。无语&#xff0c;说下前面一个问题吧&#xff0c;记录一下。 tooltip组…

【redis学习篇】主从哨兵集群架构详解

一、Redis主从架构 1.1 redis主从架构搭建 1、复制一份redis.conf文件 2、将相关配置修改为如下值&#xff1a; port 6380 pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件 logfile "6380.log" dir /usr/local/redis-5.0.3/data/6380 # 指…

Linux基础命令-groupmems管理组群的成员

Linux-usermod修改用户 Linux-useradd创建用户 Linux-userdel删除用户 Linux基础命令-chown修改文件属主 Linux基础命令-chmod修改文件权限 groupmems 命令介绍 先来看看这个命令的帮助信息是什么概念 NAME groupmems - administer members of a user’s primary group group…

Spark Tungsten

Spark Tungsten数据结构Unsafe Row内存页管理全阶段代码生成火山迭代模型WSCG运行时动态生成Tungsten (钨丝计划) : 围绕内核引擎的改进&#xff1a; 数据结构设计全阶段代码生成&#xff08;WSCG&#xff0c;Whole Stage Code Generation&#xff09; 数据结构 Tungsten 在…

如何提高代码质量

我们要写出好的代码&#xff0c;其前提是要知道“好”和“烂”定义的标准是什么&#xff0c;然后才能在写代码的时候&#xff0c;去设计一份好的代码。 如何定义“好”的代码&#xff1f; 好和坏是一个比较笼统的概率&#xff0c;代码质量高低是一个综合各种因素得到的结论&am…

scrpy学习-02

新浪微博[Scrapy 教程] 3. 利用 scrapy 爬取网站中的详细信息 - YouTubedef parse(self,response):soup BeautifulSoup(response.body,html.parser)tags soup.find_all(a,hrefre.compile(r"sina.*\d{4}-\d{2}-\d{2}.*shtmls"))#匹配日期for tag in tags:url tag.get(…