jvm调优常用参数说明

基础参数设置

1.-Xms5120m -Xmx5120m 设置初始内存和最大内存为5120m

2.-Xmn1536m 设置新生代大小为1536m

  1. -Xss512k 设置线程栈为512k

4.-XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=512m 设置元空间初始内存和最大内存为512m

5.-XX:+UseConcMarkSweepGC 指定CMS垃圾回收器作为老年代回收器,-XX:+UseParNewGC 使用Parnew垃圾回收器作为新生代回收器

参数调优
1.-XX:CMSInitiatingOccupancyFraction这个参数是指在使用CMS收集器的情况下,老年代使用了指定阈值的内存时,出发FullGC.。如:
-XX:CMSInitiatingOccupancyFraction=70 : CMS垃圾收集器,当老年代达到70%时,触发CMS垃圾回收。
2.-XX:+UseCMSInitiatingOccupancyOnly 只是用设定的回收阈值(上面指定的70%),如果不指定,JVM仅在第一次使用设定值,后续则自动调整.
如果CMSInitiatingOccupancyFraction在0~100之间,那么由CMSInitiatingOccupancyFraction决定。
否则由按 ((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0 决定
即最终当老年代达到 ((100 - 40) + (double) 80 * 40 / 100 ) / 100 = 92 %时,会触发CMS回收。
3.UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction
有一点需要注意的是:CMS并发GC不是“full GC”。HotSpot VM里对concurrent collection和full collection有明确的区分。所有带有“FullCollection”字样的VM参数都是跟真正的full GC相关,而跟CMS并发GC无关的
CMS GC要决定是否在full GC时做压缩,会依赖几个条件。其中,
第一种条件,UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction 是搭配使用的;前者目前默认就是true了,也就是关键在后者上。
第二种条件是用户调用了System.gc(),而且DisableExplicitGC没有开启。
第三种条件是young gen报告接下来如果做增量收集会失败;简单来说也就是young gen预计old gen没有足够空间来容纳下次young GC晋升的对象。
上述三种条件的任意一种成立都会让CMS决定这次做full GC时要做压缩。
CMSFullGCsBeforeCompaction 说的是,在上一次CMS并发GC执行过后,到底还要再执行多少次full GC才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩。 把CMSFullGCsBeforeCompaction配置为10,就会让上面说的第一个条件变成每隔10次真正的full GC才做一次压缩(而不是每10次CMS并发GC就做一次压缩,目前VM里没有这样的参数)。这会使full GC更少做压缩,也就更容易使CMS的old gen受碎片化问题的困扰。 本来这个参数就是用来配置降低full GC压缩的频率,以期减少某些full GC的暂停时间。CMS回退到full GC时用的算法是mark-sweep-compact,但compaction是可选的,不做的话碎片化会严重些但这次full GC的暂停时间会短些;这是个取舍。
4.CMSScavengeBeforeRemark
在CMS GC前启动一次ygc,目的在于减少old gen对ygc gen的引用,降低remark时的开销-----一般CMS的GC耗时 80%都在remark阶段
5.-XX:+ExplicitGCInvokesConcurrent
指定手动调用System.gc()的时候,不触发FullGC,触发CMSGC。主要因为用NIO/Netty框架的时候,会直接申请堆外内存,很多框架底层,为了释放mmap分配的空间,会调用System.gc()来回收
6.-XX:+CMSClassUnloadingEnabled
CMS垃圾回收时卸载无用的class类
7.排查youngGC时间过长的参数
-XX:+PrintStringTableStatistics
-XX:+PrintReferenceGC
-XX:+PrintHeapAtGC
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
8.以下为打印GC日志和OOM时dump的参数
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=xxxx/heapdump.hprof
-XX:+PrintGC
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps

性能分析工具

top
命令详解:https://www.cnblogs.com/edgedance/p/7044753.html
jstat
1.jstat -gcutil 展示gc情况
2.参考链接:https://blog.csdn.net/zhaozheng7758/article/details/8623549
jmap
1.jmap -heap 展示gc参数、堆的详情
2.jmap -histo 对象数量及⼤⼩
3.jmap -dump:format=b,file=heapDump 打印完整的堆栈信息到文件
参考链接:https://www.cnblogs.com/sxdcgaq8080/p/11089664.html
分析⼯具: IBM Heap Analyzer、Eclipse Memory Analyzer(MAT)、jhat
dump会造应⽤暂停,必须先拉出服务后再dump
jstack
jstack 获取瞬间栈运⾏情况
a.线程名称
b.nid线程id
c.线程状态:NEW、RUNNABLE、BLOCKED、WAITING、 TIMED_WAITING、TERMINATED
查找消耗CPU最⾼的线程
a.top -Hp 获取对该pid下cpu占⽤率最⾼的线程
b.printf “%x\n” [tid] 讲线程ID转为16进制
c.jstack |grep -A 30 查询该TID位置以后的30⾏的 堆栈内容
参考详情:https://m.toutiao.com/is/emoUFhD/

以8核16g的服务器为例,以下是个详细的服务启动脚本,可供参考

#!/bin/bash
#
# description: {{ APPID }} init script
# processname: vol-service
# chkconfig: 234 20 80

APPID=AppDemo
USER=deploy

#develop
SPRING_PROFILE=pro

PROJECT_NAME=AppDemo
JAR_NAME=demo.jar
SERVER_PORT=20080

BACKUP_DIR_NAME="backup"
LAST_VERSION_DIR="latest"
CONFIG_DIR_NAME="config"
JMS_PORT=9988

APP_HOME=/workspace/$PROJECT_NAME/latest
JAR_LOCATION=$APP_HOME/$JAR_NAME
NOW_DATE=$(date +"%Y-%m-%d")
SPLITER="-------------------------------------------"

#####################################################################################

#Memory configs
MEM_OPTS="-Xms7500m -Xmx7500m -Xmn3000m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=512m"

#GC OPTS
GC_OPTS="$GC_OPTS -XX:+UseConcMarkSweepGC \
-XX:+UseParNewGC \
-XX:+UseCMSCompactAtFullCollection \
-XX:CMSFullGCsBeforeCompaction=0 \
-XX:CMSInitiatingOccupancyFraction=62 \
-XX:CMSTriggerRatio=70"

#GC logging
GC_OPTS="$GC_OPTS -Xloggc:$APP_HOME/logs/gc.log"
GC_OPTS="$GC_OPTS -XX:+PrintGCDateStamps -XX:+PrintGCDetails"
GC_OPTS="$GC_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$APP_HOME/logs/heapdump.hprof"

# JMX OPTS
JMX_OPTS="$JMX_OPTS -Dcom.sun.management.jmxremote.port=$JMS_PORT \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote=true"
JVM_OPTS="$JVM_OPTS -Dfile.encoding=UTF-8 -DAPPID=$APPID \
-Dowl.metrics.kafka.servers=owl.kafka1.ttbike.com.cn:9092,owl.kafka2.ttbike.com.cn:9092,owl.kafka3.ttbike.com.cn:9092"

# SPRING PROFILES
SPRING_OPTS=" \
-Dspring.profiles.active=$SPRING_PROFILE \
-Dserver.port=$SERVER_PORT"

START_OPTS="$MEM_OPTS $GC_OPTS $JMX_OPTS $JVM_OPTS $SPRING_OPTS"
START_OPTS="$START_OPTS -Dowl.metrics.kafka.servers=owl.kafka1.ttbike.com.cn:9092,owl.kafka2.ttbike.com.cn:9092,owl.kafka3.ttbike.com.cn:9092"

#####################################################################################

function main {
  cd $APP_HOME

  for param in $@; do
    case "$param" in
            start)
                start
                ;;
            start:*)
                start $(echo $param | cut -d':' -f 2)
                ;;
            stop)
                stop
                ;;
            status)
                status
                ;;
            list)
                list
                ;;
            restart)
                stop
                start
                ;;
            backup)
                backup
                ;;
            restore)
                restore
                ;;
            restore:*)
                restore $(echo $param | cut -d':' -f 2)
                ;;
            *)
                echo $"Usage: {start|start:{develop|localTest|uatTest|product}|stop|restart|status|list|backup|restore|restore:{backup directory name}}"
                exit 0
    esac
  done
}

function start {
  echo "Starting..."
  JAVA_CMD="nohup java $START_OPTS -jar $JAR_LOCATION > /dev/null &"
  if ! [ `whoami` = "deploy" ];then
    /bin/su $USER -c "$JAVA_CMD"
  else
    /bin/bash -c "$JAVA_CMD"
  fi
  echo "Done."
}

function stop {
  echo "Stopping..."
  echo
  echo "The processes await handling."
  list
  echo
  # while read -p "Do you want to check every instance?(y/n) " -n 1 -r; do
  #   echo
  #   if [[ $REPLY =~ ^[Yy]$ ]]; then
  #     echo
      for pid in $(ps aux | grep java | grep $JAR_NAME | awk '{print $2}'); do
    #     while read -p "Are you sure to kill '$pid'?(y/n) " -n 1 -r; do
    #       echo
    #       if [[ $REPLY =~ ^[Yy]$ ]]; then
            echo "kill $pid"
            kill -9 $pid
            echo "Done."
    #         break
    #       elif [[ $REPLY =~ ^[Nn]$ ]]; then
    #         break
    #       else
    #         echo $"Supposed to be {y|Y|n|N}"
    #       fi
    #     done
      done
    #   break;
  #   elif [[ $REPLY =~ ^[Nn]$ ]]; then
  #     for pid in $(ps aux | grep java | grep $JAR_NAME | awk '{print $2}'); do
  #       echo "kill $pid"
  #       kill -9 $pid
  #       echo "Done."
  #     done
  #     break;
  #   else
  #     echo $"Supposed to be {y|Y|n|N}"
  #   fi
  # done

  list
}

function status {
  if(( $(ps aux | grep java | grep $JAR_NAME | grep -v grep | wc -l) > 0)); then
    echo "$PROJECT_NAME is running"
  else
    echo "$PROJECT_NAME is stopped"
  fi
}

function list {
  echo $SPLITER
  ps aux | grep java | grep $JAR_NAME
  echo $SPLITER
}

function backup {
  #判断jar包是否存在
  if [ ! -e $JAR_LOCATION ]; then
    echo "$JAR_LOCATION dose not exsist, please check."
    return 1
  fi
  #判断备份目录是否存在,如果不存在则创建
  if [ ! -e $BACKUP_DIR_NAME ]; then
    echo "Create directory $APP_HOME/$BACKUP_DIR_NAME"
    mkdir $BACKUP_DIR_NAME
  fi

  LAST_DIR=$BACKUP_DIR_NAME/$LAST_VERSION_DIR
  #判断最后版本目录是否存在,如果存在则清空
  if [ -e $LAST_DIR ]; then
    echo "Clear directory $APP_HOME/$LAST_DIR"
    rm -r $LAST_DIR
  fi
  echo "Create directory $APP_HOME/$LAST_DIR"
  mkdir $LAST_DIR
  #判断备份目录是否为文件夹
  if [ ! -d $BACKUP_DIR_NAME ]; then
    echo "$APP_HOME/$BACKUP_DIR_NAME is not a directory, please check your config."
    return 1
  fi

  SAVE_DIR=$BACKUP_DIR_NAME/$NOW_DATE
  #判断保存目录是否存在,如果存在则移除
  if [ -e $SAVE_DIR ]; then
    rm -r $SAVE_DIR
  fi
  #创建保存目录
  mkdir $SAVE_DIR
  #拷贝jar包
  echo "Saving jar file '$JAR_LOCATION' to '$APP_HOME/$SAVE_DIR/$JAR_NAME' ..."
  cp $JAR_LOCATION $BACKUP_DIR_NAME/$NOW_DATE
  echo "Done."
  echo "Saving jar file '$JAR_LOCATION' to '$APP_HOME/$LAST_DIR/$JAR_NAME' ..."
  cp $JAR_LOCATION $LAST_DIR
  echo "Done."
  #判断外部配置文件是否存在,如果存在,则拷贝外部配置文件
  if [ -e $CONFIG_DIR_NAME ]; then
    echo "Saving config directory '$CONFIG_DIR_NAME' to '$APP_HOME/$SAVE_DIR/$CONFIG_DIR_NAME' ..."
    cp -r $CONFIG_DIR_NAME $SAVE_DIR
    echo "Done."
    echo "Saving config directory '$CONFIG_DIR_NAME' to '$APP_HOME/$LAST_DIR/$CONFIG_DIR_NAME' ..."
    cp -r $CONFIG_DIR_NAME $LAST_DIR
    echo "Done."
  fi
}

function restore {
  if [ ! $# -eq 0 ]; then
    BACKUP_DIR=$APP_HOME/$BACKUP_DIR_NAME/$1
  else
    BACKUP_DIR=$APP_HOME/$BACKUP_DIR_NAME/$LAST_VERSION_DIR
  fi
  BACKUP_JAR=$BACKUP_DIR/$JAR_NAME
  BACKUP_CONFIG_DIR=$BACKUP_DIR/$CONFIG_DIR_NAME
  CURRENT_CONFIG_DIR=$APP_HOME/$CONFIG_DIR_NAME

  if [ ! -e $BACKUP_DIR ] || [ ! -e $BACKUP_JAR ]; then
    echo "Can not find backup file '$BACKUP_DIR' or '$BACKUP_JAR'."
    return 1
  fi
  #判断jar包是否存在,如果存在则移除
  if [ -e $JAR_LOCATION ]; then
    echo "Remove current jar '$JAR_LOCATION'"
    rm $JAR_LOCATION
    echo "Done."
  fi
  #恢复最后版本jar包
  echo "Copying '$BACKUP_JAR' to '$APP_HOME' ..."
  cp $BACKUP_JAR ./
  echo "Done."
  #判断备份配置文件是否存在
  if [ -e $BACKUP_CONFIG_DIR ]; then
    #判断现配置文件是否存在,如果存在则移除
    if [ -e $CURRENT_CONFIG_DIR ]; then
      echo "Remove current config files '$CURRENT_CONFIG_DIR' ..."
      rm -r $CURRENT_CONFIG_DIR
      echo "Done."
    fi
    #恢复最后版本配置文件
    echo "Copying '$BACKUP_CONFIG_DIR' to '$APP_HOME' ..."
    cp -r $BACKUP_CONFIG_DIR ./
    echo "Done."
  fi
}

main $@

优秀博客推荐:GC之详解CMS收集过程和日志分析:https://blog.csdn.net/baidu_33116785/article/details/53258822

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值