Java应用服务器性能问题分析

常见问题

一、内存飙高

归根结底,都是代码中某一刻的内存数据过高引起服务器内存增加。

  1. 文件导入和导出,针对文件的读写代码不合理大数据常驻内存;或者导入/导出并发量较大。
  2. 存在大表的全表查询(或者多表级联笛卡尔积查询),查出的结果集常驻内存;或者某表查询数据量较大且并发数过大(一次查询数据量*并发数)。
  3. 类对象实例化使用后未释放或者其它临时数据未释放。

二、CPU飙高

  1. 某些线程在做无阻塞的运算,简单的例子while(true)中不停的做运算,没有任何阻塞。写程序时,如果需要做很久的计算,可以适当将程序sleep下。
  2. 线程上下文切换、当启动了很多线程,而这些线程都处于不断的阻塞状态(锁等待、IO等待等)和执行状态的变化过程中。当锁竞争激烈时,很容易出现这种情况。(文件读写未使用NIO等)
  3. 序列化和反序列操作过于频繁或者目标数据量较大导致资源长时间占用。(xml/json)
  4. 频繁GC(内存异常飙高导致),访问量高时,有可能造成频繁的GC、甚至FGC。当调用量大时,内存分配过快,就会造成GC线程不停的执行,导致CPU飙高。
  5. 加密、解密。
  6. 正则表达式校验,大概原因是:Java 正则表达式使用的引擎实现是 NFA 自动机,这种引擎在进行字符匹配会发生回溯(backtracking)。

三、客户端请求阻塞

服务器CPU和内存资源看上去正常,通过JVM监控器发现很多Blocked请求线程;以下几个方法请勿出现在生产代码中,请求并发量大的情况下,很容导致请求线程阻塞。

  1. System.out.println(XX)
  2. logger.debug()
  3. e.printStackTrace()
  4. DefaultHttpClient.execute()未设置连接超时时间。

如何定位原因

一、CPU和内存情况分析

  1. 非容器部署时,通过top命令查看进程占用的CPU和内存资源,找出占用高的进程。

    image.png
  2. docker容器部署时,通过 “docker stats <容器id或者名称>”查看cpu和内存占用情况。

    imagef7594.png
  3. 通过jvisualVM或者JConsole查看JVM的CPU、内存和线程情况,如果是CPU飙高或者线程阻塞需要dump线程信息定位问题代码。

    image89810.png
    image702ba.png
  4. 线程dump分析,如果开发人员能力经验不足以肉眼分析定位问题,将dump出来的内容拷贝到txt文件中,访问在线分析平台http://fastthread.io/ ,上传dump文件,点击分析按钮,等待一小会儿后即可得出分析结果。重点关注 “CPU consuming threads”类别下的线程,点击具体某个线程进一步定位问题代码。

    imaged0dbd.png

    imageea94c.png
    image61a37.png
  5. 如果是内存问题推荐使用jmap命令dump heap信息,然后使用MAT分析工具进行分析,移步详见此文>>

    最新MAT工具下载如果是64位,直接打开会提示“Java was started but returned exit code=13”报错,解决方法重新安装64位的jdk版本。 我是直接改掉对应启动的jdk路径下的javaw.exe程序,指向64位的 jdk/bin目录下的javaw.exe。
    # 导出堆内存信息命令:jmap -dump:<dump-options> <pid> jmap -dump:format=b,file=heap.bin 6 # jmap -histo:live <pid> #统计对象count ,live表示在使用 # jmap -histo <pid> >mem.txt #打印比较有多少个对象占了多少内存的信息,一般重定向文件

题外话:如果是Spring Boot2.0应用并且配置了actuator,配置开启堆内存和线程的dump接口,通过浏览器访问即可获得dump文件。

二、tomcat日志分析

以上步骤还不能定位问题原因的,只能进行应用日志的分析。以下介绍一些使用linux命令的分析技巧。(当然如果已经搭建好ELK,可以通过ELK快速筛选异常时段的请求日志信息)

通过zabbix等监控工具,定位应用CPU或者内存异常时段,日志排查以异常时段前后延长一小段时间为主。
题外话:
1.win10支持linux子系统,不用特地为了使用linux命令装个虚拟机,详情搜索win10 linux子系统安装。

  1. 或者可以安装git客户端程序,利用其提供的Git Bash客户端即可享受Linux的相关命令。
### 分析tomcat的access日志,假设CPU或内存飙升时段是2019-08-29 16:00:00 ~16:40:00之间
## 截取指定时段的日志内容
# 1.日志时间格式为 -“2019-08-29 09:25:55606后面跟日志内容”
sed -n '/2019-08-29 16:00:00/,/2019-08-29 16:40:00/'p localhost_access_log.2019-08-29.txt > analyse_time.log
# 2.日志时间格式为 -“22/Feb/2019:15:57:00”
sed -n '/29\/Aug\/2019:16:00:00/,/29\/Aug\/2019:16:40:00/'p  localhost_access_log.2019-08-29.txt > analyse_time.log

## 获取指定时段QPS(升序排列,看最后几列最大qps数据即可评判是否并发数过大)
cat analyse_time.txt|awk '{Times[$4]++}END{for(a in Times) print a,Times[a]}'| sort -nk2|column -t > analyse_qps.log

## 获取指定时段按请求URL分组的QPS(带url参数)
cat analyse_time.txt|\
    awk '{gsub("\\:","_",$4);gsub("\\[","",$4);Times[$4]++;TimesURLs[$4":"$7]++}
      END{for(tturl in TimesURLs)
             {
               split(tturl,INFO,"\\:")
               printf("时间:%s 总访问量:%d 访问的URL:%s 访问量:%d\n",INFO[1],Times[INFO[1]],INFO[2],TimesURLs[tturl])
             }
         }'|sort -nk2 -nrk4|column -t > analyse_qps_url_by_params.log

## 获取指定时段按请求URL分组的QPS(不带url参数)
cat analyse_time.txt|\
    awk '{gsub("\\:","_",$4);gsub("\\[","",$4);split($7,UP,"\\?");Times[$4]++;TimesURLs[$4":"UP[1]]++}
      END{for(tturl in TimesURLs)
             {
               split(tturl,INFO,"\\:")
               printf("时间:%s 总访问量:%d 访问的URL:%s 访问量:%d\n",INFO[1],Times[INFO[1]],INFO[2],TimesURLs[tturl])
             }
         }'|sort -nk2 -nrk4|column -t > analyse_qps_url.log

## 获取指定时段按IP分组的访问次数
cat analyse_time.txt|awk '{IPS[$1]++}END{for(a in IPS) print a,IPS[a]}'| sort -nk2|column -t > analyse_ip_total.log

### 通过以上access日志大致可以定位出访问频繁的请求url以及进一步缩短定位的日志时段,根据上一步骤得到的请求url和时间分析具体的应用日志
## 截取指定时段的日志内容同上
sed -n '/2019-08-29 16:00:00/,/2019-08-29 16:40:00/'p app.info.log > analyse_info_time.log

## 假设查找到 16:40:30秒下的请求url"app/job/export"请求过于频繁,可以通过以下定位到具体应用日志
cat analyse_info_time.log|grep '2019-08-29 16:40:30'|grep 'app/job/export' >analyse_info_url.log

## 如果应用日志过多,比如有app.info.log~app.info.log.20 多个日志文件,如何快速定位要查询的内容在哪个日志文件中?
find app.info.log.*| xargs grep -ri "SIMPLEJOB-Thread-119" -l
## 查找指定内容并显示前后100行
cat app.info.log|grep -C 100 "SIMPLEJOB-Thread-119"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354