Tianyi's Blog Tianyi's Blog
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)

Tianyi

一直向前,永不停止
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)
  • Java

  • Golang

  • JVM的奇妙世界

    • 虚拟机1-宏观视角
    • 虚拟机2-字节码
    • 虚拟机3-输入层
    • 虚拟机4-运行时数据区
    • 虚拟机5-垃圾收集器
    • 虚拟机6-调优
    • JVM调优
    • Spring

    • Spring增强封装

    • Redis

    • MySQL

    • RabbitMQ

    • Kafka

    • 分享

    • 后端
    • JVM的奇妙世界
    tianyi
    2024-03-24
    目录
    字节码技术
    JVM 类加载器
    类的加载时机
    分析生产环境JVM启动参数
    分析生产环境Java线程
    分析SerialGC日志
    分析ParallelGC日志
    分析G1日志与CMS
    Code

    JVM调优

    关于 JVM 的核心基础知识

    1. 我们要知道Java是一个跨平台的语言,它为什么能够做到跨平台?是因为首先它是使用的class文件,在多个平台上都可以运行。为什么可以在多个平台运行?因为只需在各个平台上安装了JVM虚拟机的环境,就可以运行我们的Java程序。而对于C代码以及C++等其他语言的代码,它们都需要在对应的操作系统环境上有相应的文件,比如说C语言就会有.dll文件或者.so文件。在实际应用中,比如Java需要引用一些C语言的库的话,涉及这些语言就变得复杂,特别是在构建过程中尤其麻烦。
    2. 是JVM帮我们极大地简化了内存管理的操作。我们在编写代码时无需考虑内存管理问题。如果我们遇到内存方面的问题,只需使用JVM提供的工具排查内存管理问题,并通过修改内存参数来优化我们的Java内存管理。整个过程变得轻松而简单。

    # 字节码技术

    每个点 class 文件都是一些二进制的代码,这些代码里面包含了

    程序的运行是基于 jvm 上面的战争里面的线程运行的。

    当里面的线程进行工作时,也就是程序运行时,每调用一个新的方法。那么当前的站上的线程就会去开辟一个新的战争。每个战争上面都会包含操作数栈、局部变量,以及指向常量池的 class 引用

    # JVM 类加载器

    类加载器也就是从文件系统中加载 class 文件到 JVM 虚拟机里面的一个加载器。

    类加载器的过程是从外部文件系统中加载 class 文件到 JVM 虚拟机里面。首先,第一步是加载,就是找到 class 文件。第二步是找到 class 文件之后,需要对这个 class 文件里的内容进行验证。

    当验证完成之后,需要对里面的一些变量进行准备。准备完成之后,会将里面的引用变量指向它们真实的引用。接下来是解析,对符号引用进行解析为直接引用。然后是初始化,也就是执行构造器、静态变量赋值和静态代码块的执行。最后是使用,然后卸载。

    # 类的加载时机

    # 分析生产环境JVM启动参数

    jps -lvm

    列出 -XX 开头的参数:
    
    -XX:ErrorFile=C:\Users\willo\java_error_in_idea64_%p.log
    -XX:HeapDumpPath=C:\Users\willo\java_error_in_idea64.hprof
    -XX:ReservedCodeCacheSize=512m    // 为JIT编译器保留的代码缓存大小为512MB。
    -XX:+UseG1GC
    -XX:SoftRefLRUPolicyMSPerMB=50
    -XX:CICompilerCount=2     //设置JIT编译器线程数。
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:-OmitStackTraceInFastThrow  //在快速抛出异常时不省略堆栈信息。
    -XX:+IgnoreUnrecognizedVMOptions
    -XX:CompileCommand=exclude,com/intellij/openapi/vfs/impl/FilePartNodeRoot,trieDescend
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:MetaspaceSize=16m                           // 用于配置元空间大小和空闲空间的最小/最大比率。
    -XX:MinMetaspaceFreeRatio=10
    -XX:MaxMetaspaceFreeRatio=10
    -XX:-ShrinkHeapInSteps
    -XX:+UnlockExperimentalVMOptions
    -XX:+CreateCoredumpOnCrash
    -XX:+UseCompressedOops
    -XX:+UseCompressedClassPointers
    -XX:+UseSerialGC                      // 使用串行垃圾收集器
    -XX:MinHeapFreeRatio=10               // 堆空闲空间的最小和最大比率。
    -XX:MaxHeapFreeRatio=10
    --add-opens=java.desktop/sun.awt=ALL-UNNAMED
    --add-opens=java.desktop/sun.awt.resources=ALL-UNNAMED
    --add-opens=java.desktop/sun.awt.shell=ALL-UNNAMED
    --add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED
    
    
    -Xmx31G				 //最大堆(当前服务器的50%)	
    
    -Dfile.encoding=UTF-8
    
    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
    java -server \
         -XX:+UseConcMarkSweepGC \
         -XX:CMSInitiatingOccupancyFraction=75 \
         -XX:+UseCMSInitiatingOccupancyOnly \
         -Xss1m \
         -Dfile.encoding=UTF-8 \
         -XX:-OmitStackTraceInFastThrow \
         -XX:+HeapDumpOnOutOfMemoryError \
         -XX:HeapDumpPath=/export/xxx/log/inst-0 \
         -XX:ErrorFile=logs/hs_err_pid%p.log \
         -XX:+PrintGCDetails \
         -XX:+PrintGCDateStamps \
         -XX:+PrintTenuringDistribution \
         -XX:+PrintGCApplicationStoppedTime \
         -Xloggc:logs/gc.log \
         -Xmx31G \
         -Xms31G
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 分析生产环境Java线程

    jstack -l [进程号]

    "Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=365.64s tid=0x0000021b78eb6310 nid=0x5df0 waiting on condition  [0x00000056e18ff000]
       java.lang.Thread.State: RUNNABLE
            at java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.5/Native Method)
            at java.lang.ref.Reference.processPendingReferences(java.base@17.0.5/Reference.java:253)
            at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.5/Reference.java:215)
    
       Locked ownable synchronizers:
            - None
    
    说明
    【线程状态】
        【NEW】未启动,RUNABLE在虚拟机内执行的
        【BLOCKED】受阻塞的并等待监听器锁
        【WATING】无限等待另外一个线程执行特定的操作
        【TIMED_WATING】有期限的等待另外一个线程执行特定的操作
        【TERNUBATED】已退出的
    【deamon】守护进程
    【prio和os_prio】分别代表线程在JVM优先级,操作系统优先级
    【tid】内部线程控制结构的java内存地址(16进制)
    【nid】线程ID(16进制)
    【java.lang.Thread.State】线程栈信息
    【Locked ownable synchronizers】可以用户定位锁依赖
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 分析SerialGC日志

    -XX:+UseSerialGC -Xms1g -Xmx1g -XX:+PrintGCDetails

    GC(0) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
    GC(1) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(2) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(3) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(4) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
    GC(5) DefNew: 314560K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->34943K(34944K)
    GC(6) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(7) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
    GC(9) DefNew: 314560K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->0K(34944K)
    GC(10) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
    GC(11) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(12) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(14) DefNew: 314559K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->0K(34944K)
    GC(15) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
    GC(16) DefNew: 314559K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34943K(34944K)
    GC(17) DefNew: 314559K(314560K)->34944K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34943K(34944K)->34944K(34944K)
    GC(19) DefNew: 314560K(314560K)->0K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 34944K(34944K)->0K(34944K)
    GC(20) DefNew: 279616K(314560K)->34943K(314560K) Eden: 279616K(279616K)->0K(279616K) From: 0K(34944K)->34943K(34944K)
    生成了23374个对象
    Heap
     def new generation   total 314560K, used 81876K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000)
      eden space 279616K,  16% used [0x00000000c0000000, 0x00000000c2dd50b8, 0x00000000d1110000)
      from space 34944K,  99% used [0x00000000d3330000, 0x00000000d554fff8, 0x00000000d5550000)
      to   space 34944K,   0% used [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000)
     tenured generation   total 699072K, used 449740K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000)
       the space 699072K,  64% used [0x00000000d5550000, 0x00000000f0c83238, 0x00000000f0c83400, 0x0000000100000000)
     Metaspace       used 1025K, committed 1216K, reserved 1056768K
      class space    used 78K, committed 192K, reserved 1048576K
    
    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
    • 平均停顿时间约为19.94毫秒,总时间是199.396毫秒。
    • 生成了个对象21886
    # 分析ParallelGC日志

    -XX:+UseParallelGC -XX:ParallelGCThreads=7 -Xms1g -Xmx1g -XX:+PrintGCDetails

    GC(0) PSYoungGen: 261681K(305664K)->43503K(305664K) Eden: 261681K(262144K)->0K(262144K) From: 0K(43520K)->43503K(43520K)
    GC(1) PSYoungGen: 305647K(305664K)->43516K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43503K(43520K)->43516K(43520K)
    GC(2) PSYoungGen: 305660K(305664K)->43515K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43516K(43520K)->43515K(43520K)
    GC(3) PSYoungGen: 305659K(305664K)->43517K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43515K(43520K)->43517K(43520K)
    GC(4) PSYoungGen: 305661K(305664K)->43517K(305664K) Eden: 262144K(262144K)->0K(262144K) From: 43517K(43520K)->43517K(43520K)
    GC(5) PSYoungGen: 305661K(305664K)->43509K(160256K) Eden: 262144K(262144K)->0K(116736K) From: 43517K(43520K)->43509K(43520K)
    GC(6) PSYoungGen: 160245K(160256K)->77299K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43509K(43520K)->77299K(116224K)
    GC(7) PSYoungGen: 194035K(232960K)->100339K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 77299K(116224K)->100339K(116224K)
    GC(8) PSYoungGen: 217075K(232960K)->115498K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 100339K(116224K)->115498K(116224K)
    GC(9) PSYoungGen: 232089K(232960K)->83965K(232960K) Eden: 116591K(116736K)->0K(116736K) From: 115498K(116224K)->83965K(116224K)
    GC(10) PSYoungGen: 200526K(232960K)->45782K(232960K) Eden: 116561K(116736K)->0K(116736K) From: 83965K(116224K)->45782K(116224K)
    GC(11) PSYoungGen: 45782K(232960K)->0K(232960K) Eden: 0K(116736K)->0K(116736K) From: 45782K(116224K)->0K(116224K)
    GC(12) PSYoungGen: 116736K(232960K)->47628K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 0K(116224K)->47628K(116224K)
    GC(13) PSYoungGen: 164364K(232960K)->40157K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 47628K(116224K)->40157K(116224K)
    GC(14) PSYoungGen: 156773K(232960K)->43144K(232960K) Eden: 116616K(116736K)->0K(116736K) From: 40157K(116224K)->43144K(116224K)
    GC(15) PSYoungGen: 159880K(232960K)->43502K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43144K(116224K)->43502K(116224K)
    GC(16) PSYoungGen: 160238K(232960K)->45423K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 43502K(116224K)->45423K(116224K)
    GC(17) PSYoungGen: 162159K(232960K)->50166K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 45423K(116224K)->50166K(116224K)
    GC(18) PSYoungGen: 166902K(232960K)->45471K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 50166K(116224K)->45471K(116224K)
    GC(19) PSYoungGen: 162207K(232960K)->52582K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 45471K(116224K)->52582K(116224K)
    GC(20) PSYoungGen: 169318K(232960K)->53694K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 52582K(116224K)->53694K(116224K)
    GC(21) PSYoungGen: 53694K(232960K)->0K(232960K) Eden: 0K(116736K)->0K(116736K) From: 53694K(116224K)->0K(116224K)
    GC(22) PSYoungGen: 116736K(232960K)->51380K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 0K(116224K)->51380K(116224K)
    GC(23) PSYoungGen: 168116K(232960K)->44017K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 51380K(116224K)->44017K(116224K)
    GC(24) PSYoungGen: 160727K(232960K)->48570K(232960K) Eden: 116710K(116736K)->0K(116736K) From: 44017K(116224K)->48570K(116224K)
    GC(25) PSYoungGen: 165306K(232960K)->43119K(232960K) Eden: 116736K(116736K)->0K(116736K) From: 48570K(116224K)->43119K(116224K)
    GC(41) PSYoungGen: 176128K(261632K)->63185K(263680K) Eden: 176128K(176128K)->0K(178176K) From: 0K(85504K)->63185K(85504K)
    GC(42) PSYoungGen: 241361K(263680K)->70238K(248832K) Eden: 178176K(178176K)->0K(178176K) From: 63185K(85504K)->70238K(70656K)
    
    生成对象总数:27231
    Heap
     PSYoungGen      total 248832K, used 80240K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
      eden space 178176K, 5% used [0x00000000eab00000,0x00000000eb4c4570,0x00000000f5900000)
      from space 70656K, 99% used [0x00000000f5900000,0x00000000f9d97ba0,0x00000000f9e00000)
      to   space 88064K, 0% used [0x00000000faa00000,0x00000000faa00000,0x0000000100000000)
     ParOldGen       total 699392K, used 391038K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
      object space 699392K, 55% used [0x00000000c0000000,0x00000000d7ddfb30,0x00000000eab00000)
     Metaspace       used 1021K, committed 1216K, reserved 1056768K
      class space    used 78K, committed 192K, reserved 1048576K
    
    
    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
    • 平均停顿时间约为 6.415 毫秒,总停顿时间约为 198.935 毫秒。
      • ParallelGC和SerialGC对象分配速度和总的STW时间性能相似,但是平均STW时间相差较大
    # 分析G1日志与CMS

    参数

    -XX:+UseConcMarkSweepGC -XX:ConcGCThreads=3 -XX:MaxGCPauseMillis=50 -Xms1g -Xmx1g -XX:+PrintGCDetails
    
    -XX:+UseG1GC -XX:ConcGCThreads=3 -XX:MaxGCPauseMillis=50 -Xms1g -Xmx1g -XX:+PrintGCDetails  -Xlog:gc+time
    
    1
    2
    3
    G1 :平均 GC 时间为约 2.317 毫秒
    G1和CMS比较总结:
        G1触发GC的频率要比CMS高(一共触发了97次),但是G1平均STW时间性能快5倍,G1最大STW时间也比CMS平均快60%。
        两者内存的分配速度相差不大(前提是没有FullGC,在FullGC的场景下,G1性能还是比CMS高)
    
    1
    2
    3
    4

    压测示例对比:https://github.com/hyblog/JAVA-000/blob/main/Week_01/Node7.md

    # Code

    package Week_01;
    
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.LongAdder;
    
    public class GCLogAnalysis {
    
        private static Random random = new Random();
    
        public static void main(String[] args) {
            long startMillis = System.currentTimeMillis();
            long timeoutMillis = TimeUnit.SECONDS.toMillis(1);
            long endMillis = startMillis + timeoutMillis;
            LongAdder counter = new LongAdder();
            int cacheSize = 2000;
            Object[] cachedGarbage = new Object[cacheSize];
            while (System.currentTimeMillis() < endMillis) {
                Object garbage = generateGarbage(100*1024);
                counter.increment();
                int randomIndex = random.nextInt(2 * cacheSize);
                if (randomIndex < cacheSize) {
                    cachedGarbage[randomIndex] = garbage;
                }
            }
            System.out.println("生成对象总数:" + counter.longValue());
        }
    
        private static Object generateGarbage(int max) {
            int randomSize = random.nextInt(max);
            int type = randomSize % 4;
            Object result = null;
            switch (type) {
                case 0:
                    result = new int[randomSize];
                    break;
                case 1:
                    result = new byte[randomSize];
                    break;
                case 2:
                    result = new double[randomSize];
                    break;
                default:
                    StringBuilder builder = new StringBuilder();
                    String randomString = "randomString-Anything";
                    while (builder.length() < randomSize) {
                        builder.append(randomString);
                        builder.append(max);
                        builder.append(randomSize);
                    }
                    result = builder.toString();
                    break;
            }
            return result;
        }
    }
    
    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
    完善页面 (opens new window)
    虚拟机6-调优
    SSM-Java开发工具链

    ← 虚拟机6-调优 SSM-Java开发工具链→

    最近更新
    01
    JDK
    02-23
    02
    BadTasteCode && 优化
    09-11
    03
    Gradle 实践操作指南及最佳实践
    09-11
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 Tandy | 粤ICP备2023113440号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式