• jvm调优


    垃圾收集器

    image
    垃圾对象判断标准有:引用计数法和跟搜索法
    垃圾对象回收算法有:标记清除、复制、标记整理、分代回收

    垃圾收集器

    image
    实现表示可以搭配使用,虚线表示当内存分配失败的时候cms会退化成SerialOld。

    Serial、SerialOld、ParNew为串行。
    Paralle、ParallelOld为并行。
    cms并发。

    搭配原则:
    1、老年代选择cms,新生带只能选择串行
    2、老年代选择并行,新生代只能选择并行
    3、老年代选择串行,新生代可以选择所有类型

    串行收集器
    -XX:+SerialGC
    Serial+SerialOld:该组合是client模式下默认组合,单线程stw方式,当需要回收的时候,设置停顿标识,等所有用户线程进入安全点后开始进行垃圾回收。

    并行收集器
    -XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)
    Parallel Scavenge + Parallel Old:server模式下默认组合,高吞吐量

    cms收集器原理
    -XX:+UseConcMarkSweepGC
    ParNew + CMS + Serial Old:CMS失败降级为Serial Old。
    标记清除的算法,会产生内存碎片,通过-XX:CMSFullGCsBeForeCompaction设置多少次full gc后堆内存进行压缩(默认为0,每次都压缩)

    cms工作可以分为四步:
    1、初始标记
    stw,标记gcroot
    2、并发标记
    与用户线程共同执行,有两个问题需要解决,一个是当并发标记完成之后用户线程又会产生一些新的垃圾(浮动垃圾),这个没办法解决,只能等到下次gc时处理。另一个问题看下面:

    比如对象的引用关系a->b->c
    在初始标记阶段找出a标记为根节点
    并发标记阶段枚举a
    首先将a标记为灰色表示正在枚举a
    找到了b,接下来要开始枚举b,那么把a变黑,b变灰
    这个时候用户线程把引用关系变成了a->c
    cpu又切换到gc,继续枚举b,发现c已经不在b上了
    最终完成标记的时候c还是白色的,标识无根在引用可以清除

    如何解决这个问题,三色标记法,就是在并发标记阶段,如果改变c的引用的时候,也就是改成了a->c时,要判断一下a是否为黑色,如果a为黑色要把c标记成灰色。
    3、重新标记
    stw,把并发标记阶段发生变化的部分再次标记一下
    4、最终清除
    过程中最耗时的是并发标记,但是与用户线程共同存在。

    G1
    1、并行+并发
    2、分代收集
    3、可预测停顿
    4、将整个堆分为多个region,新生代和老年代都是一部分region的集合。
    5、跟踪各个region中垃圾回收价值(回收能回收空间大小以及需要的时间),维护一个优先级列表。
    每次进行回收的时候,根据允许的回收时间,选择回收价值最高的region进行回收
    6、跨region引用,回收时如何处理?
    有两个region A和B,A中有个对象a引用了B中对象b,当堆B进行回收的时候,枚举根节点也只是针对B的根节点,那么b对象怎么半?
    每个region维护一个remembered set,当发生引用的更新操作的时候,会记录到这个set中(当a引用b发生的时候,会把这个关系记录到B的remembered set中),当回收B的时候,不只枚举B的根节点,还要枚举B的remembered set。
    7、四个步骤:
    初始标记:标记出gcRoot,同时修改TAMS的值,该值记录哪些region是可对用户开放,可实现与用户线程并发。
    并发标记:
    重新标记:并发标记阶段发生变化的部分被记录到rememberred set logs中,该阶段把这些logs合并到remembered set中
    筛选回收:对region进行排序,按照回收价值选择性对region进行回收

    返回顶部

    jvm调优

    并行收集器通过下面两个参数让jvm自行分配其他参数
    -XX:MaxGCPauseMillis=< N > 垃圾回收最大停顿时间
    -XX:GCTimeRatio=< N > 垃圾回收停顿时间占比
    当配置了上边两个参数的时候,jvm为了达到上边的效果,会自动设置一些相关其他参数。
    推荐使用上面参数

    常见参数
    正常情况用jvm默认配置就可以,但有一些特殊应用需要自己来配置参数。

    -XX:SurvivorRatio
    eden与s0/s1的比例,这个值很重要,决定年轻代的利用率,比如默认为8比1,则年轻代占用达到百分90的时候才会发生minor gc

    -XX:MaxTenuringThreshold(最大老年代阀值)
    年轻代中对象到达多少岁时进入老年代,如果设置为0,则对象不进入年轻代直接在老年代分配,当应用老年代较多时这样可以提高效率。 linux64 jdk6默认值为15.

    -XX:NewSize
    -XX:MaxNewSize
    设置年轻代大小初始值和最大值,这个根据实际应用设置,如果年轻代临时对象多就设置大一些,一般为堆大小的三分之一、四分之一(默认三分之一)。这两个值要设置为一样的。

    -XX:+UseCMSCompactAtFullCollection
    full gc后对内存进行一次压缩,减少内存碎片

    -XX:CMSFullGCsBeforeCompaction
    设置多少次full gc后在压缩内存

    -XX:CMSInitiatingOccupancyFraction
    设置老年代使用百分比达到多少在进行full gc,默认为百分92

    -XX:+CMSScavengeBeforeRemark
    full gc前先yong gc

    年轻代年老代设置具体该设置多大?
    这个不是绝对的数字,需要根据自己的应用一点一点的去调优。大的年轻代必然导致小的年老代。大的年轻代导致minor gc次数少,小的年老代导致gull gc频繁。如果应用有大量大对象存在,适合调大年老代。
    在应用没有显着某方面特征的时候,可以本着下面的原则分配:
    2、观察应用峰值的时候看一下老年代的内存情况,如果剩余很多,可以尝试增大年轻代,前提是不会增加老年代full gc频率。
    4、栈大小默认是1m,大多数实际用不了这么多,可以尝试设置为256k,这样可以支持更大的线程数

    gc日志

    在tomcat中配置打印gc日志

    [root@01server ~]# vim /usr/local/tomcat-8.5.32/bin/catalina.sh
    JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution"
    [root@01server ~]# shutdown.sh  # 重启Tomcat
    ...
    [root@01server ~]# startup.sh 
    ...
    [root@01server ~]# ls /usr/local/tomcat-8.5.32/logs/ |grep gc.log  # 有生成gc.log这个文件代表成功
    gc.log
    [root@01server ~]#
    

    -XX:+PrintGCDetails 打印GC的详细信息
    -XX:+PrintGCTimeStamps、-XX:+PrintGCDateStamps 打印GC的时间
    -Xloggc:$CATALINA_HOME/logs/gc.log 指定GC日志文件所存放的路径
    -XX:+PrintHeapAtGC 在GC发生的前后打印堆信息
    -XX:+PrintTenuringDistribution 打印对象的年龄信息

    gc日志怎么看呢?
    image
    通过上面的日志可以分析出哪些信息?
    yong gc:回收前内存占用多少、回收后内存占用多少、回收掉了多少、有多少进入老年代、频率、每次回收耗时
    full gc:回收前内存占用多少、回收后内存占用多少、回收掉了多少、频率、每次回收耗时
    通过分析以上参数哪些不正常,可以判断出调优方案。

    调优
    jvm调优的目标是什么
    gc时间少、gc次数少、full gc频率低。
    前两个目标是相悖的,只能根据实际情况取平衡。

    OutOfMemoryException内存泄漏有哪些现象?如何解决?
    现象:gc时间越来越长、full gc越来越频繁、每次full gc老年代回收空间很少
    如何解决:
    1、生成dump文件。
    可以通过jvm参数(XX:+HeapDumpOnOutOfMemoryErro)设置当系统发生内存泄漏时生成文件。也可以通过jmap生成。
    2、分析dump文件。
    通过工具visual vm可以打开dump文件。可以查看内存各个区域情况,类加载情况,trace跟踪,线程情况,基本上所有想要的信息都可以看到。

    jdk调优命令
    jstat:实时查看内存状况。
    jmap:查看对象状况
    jstack:查看jvm栈状况
    jinfo:查看jvm配置参数信息

    内存、cpu占用高,如何排查

    查看问题进程,得到进程PID
    top -c

    查看进程里的线程明细,并手动记下CPU异常的线程PID
    top -p PID -H

    使用jdk提供jstack命令打印出项目堆栈
    jstack pid > xxx.log

    线程PID转成16进制,与堆栈中的nid对应,定位问题代码位置

    内存:
    查看内存中对象分配情况,找出业务相关的类。
    jmap -histo:live PID > xxx.log

    通过简单的统计还是没法定位问题的话,就输出内存明细来分析。这个命令会将内存里的所有信息都输出,输出的文件大小和内存大小基本一致。而且会导致应用暂时挂起,所以谨慎使用。
    jmap -dump:live,format=b,file=xxx.hprof PID

    最后对dump出来的文件进行分析。文件大小不是很大的话,使用jdk自带的jhat命令即可:
    jhat -J-mx2G -port 7170

    dump文件太大的话,可以使用console vm等工具。

    GC:
    jstat -gc PID

    这里简单介绍一下java8里面这个命令得出的列表各个列的含义:
    S0C:第一个幸存区的大小
    S1C:第二个幸存区的大小
    S0U:第一个幸存区的使用大小
    S1U:第二个幸存区的使用大小
    EC:伊甸园区的大小
    EU:伊甸园区的使用大小
    OC:老年代大小
    OU:老年代使用大小
    MC:方法区大小
    MU:方法区使用大小
    CCSC:压缩类空间大小
    CCSU:压缩类空间使用大小
    YGC:年轻代垃圾回收次数
    YGCT:年轻代垃圾回收消耗时间
    FGC:老年代垃圾回收次数
    FGCT:老年代垃圾回收消耗时间
    GCT:垃圾回收消耗总时间

    返回顶部

  • 相关阅读:
    JS原型链与instanceof底层原理
    流程关系图制作---ProcessOn从入门到精通
    VBA比较两个Excel数据的异同
    C# 通过 Quartz .NET 实现 schedule job 的处理
    C# 通过 Quartz .NET 实现Timer Job并将其注册成为Windows Service
    在.Net Framework中调用Python的脚本方法 (以VB和C#为例)
    用Python建立连接直接读取与更改Rockwell Control Logix Controller的tag值
    C#通过第三方组件生成二维码(QR Code)和条形码(Bar Code)
    如何根据条件来确定某个字段是否应该被序列化
    在Asp.Net MVC 中如何用JS访问Web.Config中appSettings的值
  • 原文地址:https://www.cnblogs.com/yanhui007/p/12929830.html
Copyright © 2020-2023  润新知