zoukankan      html  css  js  c++  java
  • JVM(五) 生产环境内存溢出调优

    1.gc配置参数

    1.1 控制台打印gc日志

    -verbose:gc
    -XX:+PrintGCDetails
    -XX:+PrintHeapAtGC(详细的gc信息)

    1.2 输出gc日志到指定文件 -Xloggc:

    (例如:  -Xloggc:C:logsgc.log)

    1.3 Gc日志分块

    -XX:-UseGCLogFileRotation
    -XX:GCLogFileSize = 8M

    1.4 指定最小堆内存 -Xms

    (例如-Xms20M指定最小堆内存为20M)

    1.5 指定最大堆内存 -Xmx

    (例如-Xms20M指定最大堆内存为20M)

    1.6 指定新生代内存大小 -Xmn

    (例如-Xmn10M指定新生代内存为10M)

    1.7 指定eden区在新生代的占比 -XX:SurvivorRatio=8

    (eden比S0,S1区比例为8:1:1)

    1.8元空间设置大小 -XX:MetaspaceSize

    初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
    -XX:MaxMetaspaceSize
    
    最大空间,默认是没有限制的。

    1.9 指定创建的对象超过多少会直接创建在老年代 -XX:PretenureSizeThreshold

    此参数只能在serial收集器和parnew收集器才有效
    
    (比如 -XX:PretenureSizeThreshold=1M)

    1.10 指定多大年龄的对象进入老年代 -XX:MaxTenuringThreshold

    (比如 -XX:MaxTenuringThreshold=15 默认也是15次)

    1.11 内存溢出时候打印堆内存快照 -XX:+HeapDumpOnOutOfMemoryError

    -XX:+HeapDumpOnOutOfMemoryError

    该配置会把快照保存在用户目录或者tomcat目录下,也可以通过 -XX:HeapDumpPath=/tmp/heapdump.dump来显示指定路径

    1.12 使用serialGC收集器 -XX:+UseSerialGC  

    1.13 使用ParNew收集器 -XX:+UseParNewGC

    1.14使用cms收集器 -XX:+UseConcMarkSweepGC

    该标志首先是激活CMS收集器。默认HotSpot JVM使用的是并行收集器。

    启动CMS多线程执行

    -XX:+CMSConcurrentMTEnabled

    指定CMS启动线程个数

     -XX:ConcGCThreads

    标志-XX:ConcGCThreads=<value>(例如:-XX:ConcGCThreads=4)

    指定老年代内存达到多少的百分比进行垃圾收集

    -XX:CMSInitiatingOccupancyFraction=70 和 -XX:+UseCMSInitiatingOccupancyOnly

    执行多少次fullgc后执行一次full gc的标记压缩算法

    默认参数场景是:-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0。这意味着每次full gc(标记清除)后,都会压缩

    开启在CMS重新标记阶段之前的清除minor gc

    -XX:+CMSScavengeBeforeRemark

    2. GC日志详情

    2.1 minor gc

    1.178: [GC (Allocation Failure) [PSYoungGen: 32768K->2688K(37888K)] 32768K->2696K(123904K), 0.0048091 secs] [Times: user=0.05 sys=0.02, real=0.00 secs]

    1.178: # 虚拟机启动以来的秒数
    [GC (Allocation Failure)
      [PSYoungGen: 32768K
    ->2688K(37888K)] # gc类型:新生代gc前占用大小 -> 新生代gc后占用大小(新生代总容量)
      32768K->2696K(123904K), # 堆空间gc前占用大小 -> 堆空间gc后占用大小(堆空间总容量)
      0.0048091 secs #gc总耗时
    ] [
    Times:
      user
    =0.05 #用户态耗费时间
      sys=0.02, #内核态耗费时间
      real=0.00 secs #整个过程实际耗费时间
    ]
    # user+sys是CPU时间,每个CPU core单独计算,所以这个时间可能会是real的好几倍。

    2.2 full gc / major gc

     7.740: [Full GC (Metadata GC Threshold) [PSYoungGen: 6612K->0K(333824K)] [ParOldGen: 10378K->15906K(95744K)] 16990K->15906K(429568K), [Metaspace: 34026K->34026K(1079296K)], 0.1300535 secs] [Times: user=0.38 sys=0.00, real=0.13 secs] 

    7.740: #虚拟机启动以来的秒数
    [
    Full GC (Metadata GC Threshold) #gc类型
    [PSYoungGen: 6612K->0K(333824K)] # 新生代gc前占用大小 -> 新生代gc后占用大小(新生代总容量)
    [ParOldGen: 10378K->15906K(95744K)] # 老年代代gc前占用大小 -> 老年代gc后占用大小(老年代总容量)
    16990K->15906K(429568K), # 堆空间gc前占用大小 -> 堆空间gc后占用大小(堆空间总容量)
    [Metaspace: 34026K->34026K(1079296K)], # 元空间gc前占用大小 -> 元空间gc后占用大小(元空间总容量)
    0.1300535 secs #gc总耗时
    ]

    [
    Times:
      user=0.05 #用户态耗费时间
      sys=0.02, #内核态耗费时间
      real=0.00 secs #整个过程实际耗费时间
    ]
    # user+sys是CPU时间,每个CPU core单独计算,所以这个时间可能会是real的好几倍。

    3. 生产环境死锁定位

     3.1 来一段死锁代码

    package com.kawa.xuduocloud.zuul;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @program: xuduocloud
     * @author: Brian Huang
     * @create: 2019-12-07 15
     **/
    @RestController
    public class JvmController {
    
        @GetMapping("/deadLock")
        public ResponseEntity<String> deadLock(){
    
            Object lockA = new Object();
            Object lockB = new Object();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String name = Thread.currentThread().getName();
                    synchronized (lockA) {
                        System.out.println(name + " got lockA,  want LockB");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                        synchronized (lockB) {
                            System.out.println(name + " got lockB");
                            System.out.println(name + ": say Hello!");
                        }
                    }
                }
            }, "thread-lock-A").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    String name = Thread.currentThread().getName();
                    synchronized (lockB) {
                        System.out.println(name + " got lockB, want LockA");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                        synchronized (lockA) {
                            System.out.println(name + " got lockA");
                            System.out.println(name + ": say Hello!");
                        }
                    }
    
                }
            }, "thread-lock-B").start();
            return  new ResponseEntity<>("deadLock", HttpStatus.OK);
        }
    
    }

    3.2 jps 查看所有的Java进程及其pid

    我这边启动是XuduocloudZuulApplication这个项目

     3.3 然后通过jstack <pid> >  <path> 导出jstack信息

    $ jstack 18640 > C:\Users\LiangHuang\Desktop\lock.txt

    打开lock.txt文件,搜索关键字“deadlock”,可以很清晰的看到锁与等待锁的信息

    Found one Java-level deadlock:
    =============================
    "thread-lock-B":
      waiting to lock monitor 0x000000001e9975b8 (object 0x00000000e8cc87d8, a java.lang.Object),
      which is held by "thread-lock-A"
    "thread-lock-A":
      waiting to lock monitor 0x0000000017e26338 (object 0x00000000e8cc87c8, a java.lang.Object),
      which is held by "thread-lock-B"
    
    Java stack information for the threads listed above:
    ===================================================
    "thread-lock-B":
        at com.kawa.xuduocloud.zuul.JvmController$2.run(JvmController.java:55)
        - waiting to lock <0x00000000e8cc87d8> (a java.lang.Object)
        - locked <0x00000000e8cc87c8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
    "thread-lock-A":
        at com.kawa.xuduocloud.zuul.JvmController$1.run(JvmController.java:34)
        - waiting to lock <0x00000000e8cc87c8> (a java.lang.Object)
        - locked <0x00000000e8cc87d8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
    
    Found 1 deadlock.

     其实定位线上死锁比较简单两个指令就可以可了  ==> jps+ jstack

    4. 生产环境CPU100%定位

     4.1 来一个cpu100%代码

    4.2 通过top指令找到cpu占用率高的程序  top

    可以看到pid为7977java进程占用最高

    4.3 通过ps指令查找最展cpu的子线程  ps -mp pid -o THREAD,tid,time

    可以看到tid为8072的子线程占用率最高

     4.4 通过jstack导出日志 分析日志   jstack pid >a.txt

    查找方式最将刚才tid从十进制转为16进制   8072  => 1f88

    在日志文件搜索关键字 ‘1f88’ 查找nid包含这个就是对应cpu100%的日志信息 , 如下

     生产环境cpu飙高三个指令就可以了  => top + ps + jstack

    5.生产环境堆内存溢出定位

    环境配置 -verbose:gc -XX:+PrintGCDetails -Xloggc:C:UsersLiangHuangDesktopupgc.log -XX:-UseGCLogFileRotation -XX:GCLogFileSize=5M -XX:HeapDumpPath=C:UsersLiangHuangDesktopheapdump.dump -XX:+HeapDumpOnOutOfMemoryError

    可以在oom时候生产快照,方便我们使用工具分析

    5.1 内存溢出的定位

     5.1.1 上代码 内存溢出

    5.1.2 jps查看程序pid

    5.1.3 jstat 查看jvm内存使用情况 jstat -gctuil pid

    5.1.4  oracle jdk 工具  jvisualvm.exe 分析快照

    jvisualvm.exe导入dump文件

    可以看到异常的线程,打开Show Threads 详细信息,搜索关键字“http-nio-82-exec-1”

    5.2 内存泄漏的定位

     5.2.1 上代码内存泄漏

    5.2.2 jps 查看程序pid

    5.2.3 jstat查看内存情况 jstat -gcutil pid

     

     可以看到eden区和老年代  没有被回收,内存泄漏的导致不能被回收

    5.2.4  oracle jdk 工具  jvisualvm.exe 分析快照

    jvisualvm.exe导入dump文件

    6.总结JVM

    6.1. jvm常用的调优参数

    -Xmx 堆内存最大值 -Xmx2g
    -Xms 堆内存最小值 -Xms2g
    -Xmn 新生代大小 默认时堆的1/3
    -xss 线程栈空间大小 -Xss256k

    6.2. jvm运行时数据区域由哪几个部分组成,各自作用

    线程共享
    堆: new出来的对象放在堆中,对象可能会栈上分配(内存逃逸分析)
    元空间/方法区:class对象,常量,静态变量,运行时常量池
    
    内存独占
    栈:栈内部由栈帧组成,先进后出,栈帧(局部变量表,操作数栈,动态链接,返回地址)
    PC寄存器(程序计数器):指向当前线程执行到多少行代码
    本地方法栈: native修饰的方法

    6.3. gc算法有哪些,gc收集器有哪些

    gc算法==
    分代算法
    复制算法
    标记清除
    标记压缩
    引用计数
    可达性分析法
    
    gc收集器==
    young区 (Serial,ParNew,Parallel Scavenge)
    old区(SerialOld,ParallelOld,CMS)
    G1

    6.4. GC Roots的对象有哪些

    局部变量
    静态变量
    本地方法栈
    静态常量 static final

    6.5. 垃圾收集器各自优缺点

    Serial:单线程收集 非单核服务器 stw比较长
    Parnew: 多线程收集 多核线程比较快
    PS: 可控吞吐量 (用户线程执行时间)/(用户线程执行时间+gc执行时间)
    CMS: 初始标记(标记和gc roots相关的对象 有swt),
    并发标记(标记被初始标记的对象关联的对象 和用户线程一起执行),
    重新标记(新生代可达引用到老年代的对象,刚进入老年代的对象 stw),
    并发清除 (和用户线程一起执行,清除老年代没有被标记的对象)

    6.6.  full gc,minor gc,major gc,stw

    minor gc新生代gc
    major gc老年代gc
    full gc= minor gc + major gc
    stw: stop the word 停止其他所有的用户线程

    6.7. jvm中一次完整的gc流程 ygc => fgc ,对象如何晋升到老年代

    正常流程===
    经过15次ygc(复制算法)晋升到老年代
    大对象直接进入到老年代
    
    非正常=== 
    动态年龄 (s区
    50%以上对象年龄 > s区平均年龄则进阶老年代,如果老年代空间不足则发生full gc) 空间担保 (s0或s1放不下这些对象,会进行于此空间担保(老年代剩余空间大于历代s区进阶的平均值则担保成功))如果担保 失败则发生full gc 元空间/方法区不足也会发生full gc,但不会被垃圾收集器回收

    6.8.  内存泄漏判断

    对象不能被gc回收就会导致内存泄漏
    jstack 发生gull gc后,新生代和老年代的占用情况,如果占用的空间没有降低则可以判断放生内存泄漏
    如果fgc放生频率远远高于ygc则发生了内存泄漏

    6.9. 内存溢出判断

    后台没写分页,大数据量,内存溢出报错后,对象会被回收,整个服务任然可用
    内存泄漏导致的内存溢出,泄漏的对象不会被回收,知道我们的整个堆内存被占满,导致整个服务不可用
    
    打印dump文件,分析快照,查找大对象

    6.10.  java中的引用类型有哪些

    强引用:Object o = new Objetc(); gc不会回收强引用对象
    软引用:SoftReference 对内存占满时就会这里面的对象(这个一般用来做缓存)
    弱引用:WearkReference 只能存在下一次gc之前 (minor gc,major gc发生就会被回收)
    虚引用:Object o = new Object(); o = null; 提醒gc来回收这个对象
  • 相关阅读:
    视频遮挡问题
    calc兼容性
    javascript变量声明提升
    jquery插件
    prop和attr在 jquery的
    onclick防止冒泡和json对象放入
    git 入门
    去掉ie滚动条兼容性
    单页面应用程序(SPA)
    swiper轮播图插件
  • 原文地址:https://www.cnblogs.com/hlkawa/p/11965522.html
Copyright © 2011-2022 走看看