一次线上问题排查所引发的思考
前言
之前或多或少分享过一些内存模型、对象创建之类的内容,其实大部分人看完都是懵懵懂懂,也不知道这些的实际意义。
直到有一天你会碰到线上奇奇怪怪的问题,如:
线程执行一个任务迟迟没有返回,应用假死。
接口响应缓慢,甚至请求超时。
CPU 高负载运行。
这类问题并不像一个空指针、数组越界这样明显好查,这时就需要刚才提到的内存模型、对象创建、线程等相关知识结合在一起来排查问题了。
正好这次借助之前的一次生产问题来聊聊如何排查和解决问题。
生产现象
首先看看问题的背景吧:
我这其实是一个定时任务,在固定的时间会开启 N 个线程并发的从 Redis 中获取数据进行运算。
业务逻辑非常简单,但应用一般涉及到多线程之后再简单的事情都要小心对待。
果不其然这次就出问题了。
现象:原本只需要执行几分钟的任务执行了几个小时都没退出。翻遍了所有的日志都没找到异常。
于是便开始定位问题之路。
定位问题
既然没办法直接从日志中发现异常,那就只能看看应用到底在干嘛了。
最常见的工具就是 JDK 自带的那一套。
这次我使用了jstack来查看线程的执行情况,它的作用其实就是 dump 当前的线程堆栈。
当然在 dump 之前是需要知道我应用的 pid 的,可以使用jps -v这样的方式列出所有的 Java 进程。
当然如果知道关键字的话直接使用ps aux|grep java也是可以的。
拿到pid=1523了之后就可以利用jstack 1523 > 1523.log这样的方式将 dump 文件输出到日志文件中。
如果应用简单不复杂,线程这些也比较少其实可以直接打开查看。
但复杂的应用导出来的日志文件也比较大还是建议用专业的分析工具。
我这里的日志比较少直接打开就可以了。
因为我清楚知道应用中开启的线程名称,所以直接根据线程名就可以在日志中找到相关的堆栈:
所以通常建议大家线程名字给的有意义,在排查问题时很有必要。
其实其他几个线程都和这里的堆栈类似,很明显的看出都是在做 Redis 连接。
于是我登录 Redis 查看了当前的连接数,发现已经非常高了。
这样 Redis 的响应自然也就变慢了。
接着利用jps -v列出了当前所以在跑的 Java 进程,果不其然有好几个应用都在查询 Redis,而且都是并发连接,问题自然就找到了。
解决办法
所以问题的主要原因是:大量的应用并发查询 Redis,导致 Redis 的性能降低。
既然找到了问题,那如何解决呢?
减少同时查询 Redis 的应用,分开时段降低 Redis 的压力。
将 Redis 复制几个集群,各个应用分开查询。但是这样会涉及到数据的同步等运维操作,或者由程序了进行同步也会增加复杂度。
目前我们选择的是第一个方案,效果很明显。
本地模拟
上文介绍的是线程相关问题,现在来分析下内存的问题。
以这个类为例:
https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/oom/heap/HeapOOM.java
1publicclassHeapOOM{
2
3publicstaticvoidmain(String[] args){
4List list =newArrayList<>(10) ;
5while(true){
6list.add("1") ;
7}
8}
9}
启动参数如下:
1-Xms20m
2-Xmx20m
3-XX:+HeapDumpOnOutOfMemoryError
4-XX:HeapDumpPath=/Users/xx/Documents
为了更快的突出内存问题将堆的最大内存固定在 20M,同时在 JVM 出现 OOM 的时候自动 dump 内存到/Users/xx/Documents(不配路径则会生成在当前目录)。
执行之后果不其然出现了异常:
同时对应的内存 dump 文件也生成了。
内存分析
这时就需要相应的工具进行分析了,最常用的自然就是 MAT 了。
我试了一个在线工具也不错(文件大了就不适合了):
http://heaphero.io/index.jsp
上传刚才生成的内存文件之后:
因为是内存溢出,所以主要观察下大对象:
也有相应提示,这个很有可能就是内存溢出的对象,点进去之后:
看到这个堆栈其实就很明显了:
在向 ArrayList 中不停的写入数据时,会导致频繁的扩容也就是数组复制这些过程,最终达到 20M 的上限导致内存溢出了。
更多建议
上文说过,一旦使用了多线程,那就要格外小心。
以下是一些日常建议:
尽量不要在线程中做大量耗时的网络操作,如查询数据库(可以的话在一开始就将数据从从 DB 中查出准备好)。
尽可能的减少多线程竞争锁。可以将数据分段,各个线程分别读取。
多利用CAS+自旋的方式更新数据,减少锁的使用。
应用中加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp参数,在内存溢出时至少可以拿到内存日志。
线程池监控。如线程池大小、队列大小、最大线程数等数据,可提前做好预估。
JVM 监控,可以看到堆内存的涨幅趋势,GC 曲线等数据,也可以提前做好准备。
总结
线上问题定位需要综合技能,所以是需要一些基础技能。如线程、内存模型、Linux 等。
当然这些问题没有实操过都是纸上谈兵;如果第一次碰到线上问题,不要慌张,反而应该庆幸解决之后你又会习得一项技能。
欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Maven学习笔记(一)
前言在2013年左右的时候Maven工具就非常火了,在IT技术日新月异的今天,Maven工具热度依然不减,可见Maven依旧是相当受欢迎的项目管理框架之一了,废话不多说,开启Maven学习之旅。 Maven是什么? 当你利用搜索引擎搜索Maven会出来很多名词:对象模型、标准集合、依赖管理系统这是啥啊?差不多得你会用了Maven才会理解这些名称吧,我找到了一个博主的博客blog.csdn.net/qq_27376871/article/details/51612742他做了不错的总结,Maven可以干什么: 1 . 帮你下载jar包 maven项目会有一个 pom.xml文件, 在这个文件里面,只要你添加相应配置,他就会自动帮你下载相应jar包,不用你铺天盖地的到处搜索你需要的jar包了 。maven都会通过:项目名-项目模块-项目版本来maven在互联网上的代码库中下载相应jar包。寻找jar的地址:http://search.maven.org所以这就是maven的功能之一,帮你下载jar包 2 . 寻找依赖,帮你下载依赖 寻找jar包是第一基本功能,寻找依赖在这个是在这个基础上的...
- 下一篇
Python那么火,到底能用来做什么?我们来说说Python3的主要应用
如果您正在考虑学习Python,或者您最近刚开始学习, 您可能会问自己:“我到底可以用Python做什么?” 这是个棘手的问题,因为Python有很多用途。 但是随着时间的推移, 我发现Python主要可用于一下三个方面: Web开发 数据科学——包括机器学习、数据分析和数据可视化 脚本编写 我们就依次来看看吧。 一、Web开发 像Django和Flask这样基于Python的Web框架最近在web开发中变得非常流行。 这些web框架帮助您用Python创建服务器端代码(后端代码)。 这些代码在您的服务器上而不是在用户设备以及浏览器上(前端代码)运行。 如果您不熟悉后端代码和前端代码之间的区别,请参阅下面脚注。 但是,等等,我为什么需要web框架呢? 那是因为web框架让构建通用后端逻辑变得更简单了。 这包括把不同的URL映射到Python代码块、处理数据库和生成用户在浏览器中看到的HTML文件。 我应该用哪个Python web框架? Django和Flask是两种最流行的Python web框架。 如果您刚刚开始学习,那么可以用它们中的任何一个。 Django和Flask有什么区别...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS关闭SELinux安全模块
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7设置SWAP分区,小内存服务器的救世主