本文转载自 crossoverJie 的b博客 https://www.cnblogs.com/crossoverJie/p/9282065.html

前言

之前或多或少分享过一些内存模型对象创建之类的内容,其实大部分人看完都是懵懵懂懂,也不知道这些的实际意义。

直到有一天你会碰到线上奇奇怪怪的问题,如:

  • 线程执行一个任务迟迟没有返回,应用假死。
  • 接口响应缓慢,甚至请求超时。
  • 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

public class HeapOOM {

    public static void main(String[] args) {
List<String> list = new ArrayList<>(10) ;
while (true){
list.add("1") ;
}
}
}

启动参数如下:

-Xms20m
-Xmx20m
-XX:+HeapDumpOnOutOfMemoryError
-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 相关的知识点,感兴趣的朋友可以一起维护。

地址: https://github.com/crossoverJie/Java-Interview

作者: crossoverJie

出处: https://crossoverjie.top

java运维: 一次线上问题排查所引发的思考的更多相关文章

  1. Java开发必须掌握的线上问题排查命令

    作为一个合格的开发人员,不仅要能写得一手还代码,还有一项很重要的技能就是排查问题.这里提到的排查问题不仅仅是在coding的过程中debug等,还包括的就是线上问题的排查.由于在生产环境中,一般没办法 ...

  2. java:线上问题排查常用手段(转)

    出处:java:线上问题排查常用手段 一.jmap找出占用内存较大的实例 先给个示例代码: import java.util.ArrayList; import java.util.List; imp ...

  3. Java线上问题排查思路及Linux常用问题分析命令学习

    前言 之前线上有过一两次OOM的问题,但是每次定位问题都有点手足无措的感觉,刚好利用星期天,以测试环境为模版来学习一下Linux常用的几个排查问题的命令. 也可以帮助自己在以后的工作中快速的排查线上问 ...

  4. Java架构师线上问题排查,这些命令程序员一定用得到!

    Java架构师线上问题排查,这些命令程序员一定用得到! 线上问题排查,以下场景,你遇到过吗? 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服 ...

  5. Java线上问题排查神器Arthas实战分析

    概述 背景 是不是在实际开发工作当中经常碰到自己写的代码在开发.测试环境行云流水稳得一笔,可一到线上就经常不是缺这个就是少那个反正就是一顿报错抽风似的,线上调试代码又很麻烦,让人头疼得抓狂:而且deb ...

  6. 【转】又一次线上 OOM 排查经过

    又一次线上OOM排查经过 最近线上一个服务又出现了频繁Full GC的情况,导致提供的业务经常超时.问题出现非常不稳定,经过两周的时候,终于又捕捉到了一次Full GC,于是联系运维做Heap Dum ...

  7. BTrace:线上问题排查工具

    BTrace简介 GitHub地址:BTrace 下载地址:v1.3.11.3 官方使用教程:Btrace使用教程 使用场景 BTrace 是一个事后工具,所谓事后工具就是在服务已经上线了,但是发现存 ...

  8. 线上问题排查神器 Arthas

    线上问题排查神器 Arthas 之前介绍过 BTrace,线上问题排查神器 BTrace 的使用,也说它是线上问题排查神器.都是神器,但今天这个也很厉害,是不是更厉害不好说,但是使用起来非常简单.如果 ...

  9. JVM 线上故障排查基本操作--CPU飙高

    JVM 线上故障排查基本操作 CPU 飚高 线上 CPU 飚高问题大家应该都遇到过,那么如何定位问题呢? 思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程.然后 ...

随机推荐

  1. MVC使用记录

    如何获得MVC中,控制器和方法名字.这可以用于给当前选定菜单加个选定样式 获取控制器名称:(在View中写法) ViewContext.RouteData.Values["controlle ...

  2. Baker Vai LightOJ - 1071 (MCMF)

    在个给出的矩阵从,从左上角走到右下角,然后再从右下角走到左上角,两次不能经过想同的点,每个点都有一个价值,问最大的价值是多少. 可以把原来的问题化简成从左上角走两条路到右下角,然后把价值加起来,然是这 ...

  3. 20165223 《JAVA程序设计》第四周学习总结

    20165223 <JAVA程序设计>第四周学习总结 教材学习内容总结 第五章要点 子类与继承 成员变量的隐藏和方法重写 super关键字 继承与多态 abstract类与abstract ...

  4. redis健康检查与故障转移

    哨兵三个定时监控任务 每隔10s每隔sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构 每隔2S,每个sentinel节点会向redis数据节点的__sentiel__:hell ...

  5. ACM-ICPC 2017 Asia Xi'an A XOR (线性基+线段树思想)

    题目链接 题意;给个数组,每次询问一个区间你可以挑任意个数的数字异或和 然后在或上k的最大值 题解:线性基不知道的先看这个,一个线性基可以log的求最大值把对应去区间的线性基求出来然后用线段树维护线性 ...

  6. 区块链使用Java,以太坊 Ethereum, web3j, Spring Boot

    Blockchain is one of the buzzwords in IT world during some last months. This term is related to cryp ...

  7. gallery

    效果如下 目录如下 代码如下: //index.html <!DOCTYPE html> <html> <head> <meta charset=" ...

  8. mybatis 的批量更新操作sql

    转: mybatis 的批量更新操作sql 2018年07月23日 10:38:19 海力布 阅读数:1689   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  9. SpringCloud-初识

    说道SpringCloud,原来就去了解过,也有很大兴趣,只是当初不知道这是个什么东西.在它之前,我学习Spring,在官网肆无忌惮的逛的时候,发现了SpringBoot,那个时候就打算开始学习Spr ...

  10. C# 正则表达式中的顺序环视和逆序环视

    环视结构不匹配任何字符,只匹配文本中的特定位置. 顺序环视:从左向右查看文本,尝试匹配子表达式,如果能够匹配则返回匹配成功信息.顺序环视使用「 (?=...) 来标识」,例如「 (?=\d) 」,它表 ...