首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >遇到个小BUG之后

遇到个小BUG之后

作者头像
用户3467126
发布于 2020-08-27 03:55:33
发布于 2020-08-27 03:55:33
36200
代码可运行
举报
文章被收录于专栏:爱编码爱编码
运行总次数:0
代码可运行

事故开始

时间在回到一周前,测试跑过来跟我说:压测500w同步数据失败了。我保持以往的态度,莫慌莫慌,多大点事儿,然后打开运行日志,然后一看居然是内存不足,如下图:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 12288 bytes for committing reserved memory.

做了那么久Java开发还是第一次遇到这种情况,然后free命令,查看一下服务器剩余内存,发现有5个G剩余,然后想一下,这不应该啊。然后自己也来测试一下,重新启动同步程序,一开始还是没有问题。过了10分钟后,Linux命令free查看剩余内存,10s钟刷新一下,内存消耗200M,到最后没有错10min钟后就跑蹦了。这时候我想了一下,应该遇到内存泄漏OOM了(真倒霉)。

第一次尝试

1.1、分析定位应用

从刚才的分析来看肯定是OOM导致的了,那要定位到底是哪个服务占用内存是一直飙升的。

Linux下运行如下命令可以查看,内存消耗最多的前10个进程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ps auxw|head -1;ps auxw|sort -rn -k4|head -10

从图中基本锁定的了是第一个应用A或者第二个应用B

1.2、大数据量分小批次执行

查看运行日志后,发现A应用的数据库连接报错。mybatis进行批量update时报错了,打印了sql。这时候我的悬着的心突然放了下来,有种幸亏你报错了的感觉!!执行了打印的sql发现执行批量update操作1w条数据导致异常,估计是超时导致,但是该sql的where条件是主键,应该比较快的。所以现在只能降低数据量了,最终改为1000条数据进行操作速度就跟上了。改好问题然后测试环境执行一下,没有报错,内存也没有什么异常,下班!!!

第二天打开日志文件一看update还是报错,我擦傻眼了,服务又停了,又是内存溢出。

第二次尝试

服务B多线程通过fegin调用服务A中的接口,从服务A的报错日志来看是批量update导致mysql死锁的,然后搜索一下如何解决,类似如图:

发现了这位兄台的分析比较符合我的情况:https://www.cnblogs.com/pufeng/p/12069835.html

问题总结:应该是由于多线程同时修改同一条数据导致mysql死锁。比如你正在update某会员C的信息,另一条线程也在udpate该会员的信息这就会导致死锁。

2.1、加分布式锁

分布式锁的方式有很多,这里我的项目中用的jedis,各位可以自己搜索redis分布锁。类似逻辑如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           boolean isLock = false;
            try {
                //调用路由服务同步数据
                while (!isLock) {
                    //获取分布式锁(20秒过期)
                    isLock = this.jedisClient.tryGetDistributedLock(RedisKey.CAS_SELLER_TRADES + sellerTable.getUserName(), sellerTable.getUserName(), 20);
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        logger.error("同步交易|获取分布式锁异常:" + e.getMessage());
                    }
                }
                long timeMillis = System.currentTimeMillis();
                 //TODO 执行业务,调用服务A中的同步接口
                  //***********************
                logger.info("同步交易|卖家账号:{}|调用路由同步数据耗时:{}毫秒", sellerTable.getUserName(), System.currentTimeMillis() - timeMillis);
            } catch (Exception e) {
                logger.error("同步交易|卖家账号:{}|线程异常:{}", sellerTable.getUserName(), ExceptionUtil.getStackTrace(e));
            } finally {
                //每条线程完成就释放
                if (isLock) {
                     this.jedisClient.releaseDistributedLock(RedisKey.CAS_SELLER_TRADES + sellerTable.getUserName(), sellerTable.getUserName());
                }
            }

然后重新执行应用程序,以为现在就应该ok了吧。但30分钟后,服务A居然还是停了,这次报错是关于fegin hystrix报错,结合日志打印的应该是服务接口处理时间过长(主要是入库update耗时),导致大量线程请求堆积到服务A接口。

2.2、改变分布式锁位置

从上面可知道,目前是因为大量线程堆积到了服务A,服务A受不了就报错给你看。

那么最简单的办法应该就是将分布式锁移到服务B调用服务A接口的位置,加上信号量Semaphore对线程数进行控制应该就可以了。此处做法就与上面相同,各位看需求是否需要加信号量Semaphore控制线程数量。

然后部署继续进行测试观察。。。跑了2个200w数据,大约20分钟,服务A还是内存溢出了,日志报错,这次报错是mysql的某一条select查询超时。

第三次尝试

在Navicat中执行日志的sql,居然耗时20s多都还没返回,估计问题就是在这里了。

3.1、加索引

因为表中的数据已经有400w数据,所以此次查询的条件没有索引,这样子当然慢的。所以将服务A该接口的所有操作的sql语句都检查一遍是否需要加联合索引或者普通索引或者主键之类的。

注意:索引的最左原则。最好是通过explain分析一下sql语句是否使用了索引。

explain分析sql语句:

https://www.cnblogs.com/tufujie/p/9413852.html

3.2、减少非必要的列返回

此处就不多说了,特别注意的是有一些字段所占的数据是特别大的,如果没有业务必要的话尽量不要查询返回。

到此,应该不会出现什么问题了吧,然后部署测试环境。大约过了20分钟左右,内存还是溢出了,这次日志文件都没有报错。。。。。这就尴尬了!!!!

第四次尝试

经过请教大神,这种情况应该是对象引用没有释放才会导致堆栈溢出的。

本地运行相关服务程序,windows可以打开jdk目录下的jvisualvm.exe工具、jconsole.exe工具对内存进行监控看看哪一步出现的问题。至于这两个工具怎么用可以网上搜索一下哈。

Linux下可以使用命令进行监控:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
jstat -gc [pid] [ms]

4.1、jstat命令分析

相关参数的含义如下:

  • S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
  • EC、EU:Eden区容量和使用量
  • OC、OU:年老代容量和使用量
  • PC、PU:永久代容量和使用量
  • YGC、YGT:年轻代GC次数和GC耗时
  • FGC、FGCT:Full GC次数和Full GC耗时

主要留意图中的两个参数OC,OU。如果OC一直飚升,OU一直往上飚没有回落的情况的话,那就是老年代溢出了,这就表明你的代码中有对象一直引用着没有释放。

多观察几个周期,OU有规律性的下降就是正常,如下图:

关于该jstat 命令可以参考文章:https://www.cnblogs.com/qmfsun/p/5601734.html

4.2、内存溢出解决方案

各自写的代码不一样,这里就很难判断的。有同学说可以查看内存溢出文件分析一下就知道了。的确没有错,如下链接:https://www.cnblogs.com/lemon-flm/p/11599505.html

但是,我是直接将所有用到的对象、集合等都给清空并置空。这样子100%不会溢出了吧。

最终稳定地运行了!!花费了2天时间终于把它搞定了,差点掏空了我所有的Java知识。主要是代码写的不够严谨导致的,要多学习多思考!!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
线上java JVM问题排查
下面是一个老系统,代码写的有点问题导致出现这样一个JVM占比过高的问题,正常情况下也就是CPU负载不高的时候21:00左右的,也有30万,但是再多一点30几万就是阈值,就会出现堆积。
Java架构师必看
2021/07/13
1.4K0
阿里腾讯后台社招面经(已拿offer)
博主17届双非一本毕业, 主要是搞Java开发的, 没有大厂经验. 2020 自己也马上快3年工作经验了. 如果再不找找机会进大厂深造一下, 后面的竞争力和个人的提升将会更难.因此在现在公司磨砺了两年之后, 开始向大厂迈进~ 这篇博客主要是想分享一下自己在面试过程中所遇到的问题,相对比较坎坷,前后经历了3个多月.希望大家也能在找工作的过程中,坚持下来!
九灵
2020/06/23
4.5K1
JVM|02内存模型
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。 根据java虚拟机的规范,我们可以将JVM的内存分为五大块
微笑的小小刀
2019/09/03
5190
JVM|02内存模型
Jstat命令实战
在JVM命令行工具中有很多优秀的工具,本文章主要详细地介绍Jstat命令行工具的案例应用和实战。搭建好JDK的环境后,就可以直接使用Jstst命令行的工具。下面主要从三个维度分别阐述下Jstat命令行工具,具体分别是类加载,垃圾收集以及JIT编译。
无涯WuYa
2023/02/28
5110
Jstat命令实战
JVM学习系列学习四
本文是《JVM学习系列》中的第三篇文章。如果想系统的学习,建议从本教程第一篇开始看。
凯哥Java
2019/07/02
4250
Java线上问题排查思路及Linux常用问题分析命令学习
之前线上有过一两次OOM的问题,但是每次定位问题都有点手足无措的感觉,刚好利用星期天,以测试环境为模版来学习一下Linux常用的几个排查问题的命令。 也可以帮助自己在以后的工作中快速的排查线上问题。
一枝花算不算浪漫
2018/12/25
1.3K0
大型分布式网站架构设计与实践笔记(第一次看)
互联网安全架构 常见的web攻击手段 xss攻击(跨站脚本攻击 Cross Site Scripting) 攻击原理: 用户输入的数据变成了代码 防范: 需要对用户输入的数据进行html转义处理 CSRF攻击(跨站请求伪造 cross site request forgery) 攻击原理: 盗取受信任用户身份,利用该身份攻击存在csrf漏洞的网站进行攻击 攻击者要完成CSRF攻击需要用户做到以下几点: 登录受信任的站点A,并在本地生成cookie 在不(清除站点A的cookie)的情况下,访问
lin_zone
2019/02/22
4600
TSF微服务中java应用出现性能问题排查思路
目前采用微服务架构已经逐渐成为企业架构的标准范式,而大多微服务是基于Spring Cloud框架来进行应用的构建的,所以在开发实践中,甚至生产环境中,会遇到java相关问题,例如系统运行变慢、内存OOM,堆栈异常等问题,这里结合我之前的一些实践提供一些相关工具,和大家一起分享我们的诊断思路和解决技巧。
邓愉悦
2020/11/03
1.2K0
JVM内存溢出问题排查
内存溢出 out of memory : 通俗理解就是内存不够用了,是我们工作当中经常会遇到的问题,内存溢出有可能发生在正常的情况下,而非代码层面问题导致,比如高并发下,大量的请求占用内存,垃圾回收机制无法进行回收,而导致的内存溢出,这种情况就需要我们去调整架构了。一但出现内存溢出问题,我们需要快速定位并解决,尤其是生产环境,所以针对内存溢出问题,我们需要掌握一些常用的排查工具,针对不同场景、现象有快速排查思路。引起内存溢出的原因有很多种,常见的有以下几种:
霍格沃兹测试开发Muller老师
2022/12/04
2.2K0
JVM调优工具详解
学习了JVM的一些调优工具为大家分享一下,现在把学习笔记总结记录一下,如果记录有些错误,还望指出。
全栈程序员站长
2022/09/04
4510
JVM调优工具详解
90%的人会遇到性能问题,如何用1行代码快速定位?
今天,齐光将会基于之前列举的众多指标,给出一些常见的调优分析思路,即:如何在众多异常性能指标中,找出最核心的那一个,进而定位性能瓶颈点,最后进行性能调优。整篇文章会按照代码、CPU、内存、网络、磁盘等方向进行组织,针对对某一各优化点,会有系统的「套路」总结,便于思路的迁移实践。
Datawhale
2019/12/09
8900
90%的人会遇到性能问题,如何用1行代码快速定位?
性能监控之常见JDK命令行工具整理
格式:-XX:[+-]表示启用或者禁用name属性比如:-XX:+UseConcMarkSweepGC 启用CMS垃圾回收器 -XX:+UseG1GC 启用G1垃圾回收器
高楼Zee
2019/07/17
9710
性能监控之常见JDK命令行工具整理
JAVA 线上故障排查完整套路!牛掰!
线上故障主要会包括 CPU、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如 jstack、jmap 等工具也是不囿于一个方面的问题的,基本上出问题就是 df、free、top 三连,然后依次 jstack、jmap 伺候,具体问题具体分析即可。
程序猿DD
2020/05/26
3.1K0
JAVA 线上故障排查完整套路!牛掰!
线上问题排查方法
1 OOM问题 1.1 堆内存OOM 1.2 栈内存OOM 1.3 栈内存溢出 1.4 GC OOM 1.5 元空间OOM 2 CPU100%问题 3 接口超时问题 4 索引失效问题 5 死锁问题 6 磁盘问题 7 MQ消息积压问题 8 调用接口报错 8.1 返回401 8.2 返回403 8.3 返回404 8.4 返回405 8.5 返回500 8.6 返回502 8.7 返回504 优化的方向是: 1.优化索引 2.优化sql语句 3.异步处理 4.批量处理 5.远程调用 6.避免大事务 7.锁粒度 8.分页处理 9.加缓存 10.分库分表 11.辅助功能 11.1 开启慢查询日志 11.2 加监控 11.3 链路跟踪
oktokeep
2024/11/29
2110
【Java面试】第二章:P5级面试
特别说明:本文薪资目标为22k,别纠结于薪资能不能到22k,在到达22k之前,这些博文里的专业技能方面都要懂吧,如果连这些基础的东西都不懂,你到了22k,估计也是很水的存在了。除此之外,拿22薪你还需要具备,能独立完成一个复杂模块的需求分析、方案设计和最终落地实现,寻找更优的设计和解决方案,积极优化慢 SQL、慢服务,具备排查问题的能力,遇到线上问题能及时定位和修复上线,例如:数据库死锁、服务器宕机、服务器 Full GC 频繁等。合理分配需求,做好进度把控、风险评估、Code Review。
全栈程序员站长
2022/08/01
8210
【Java面试】第二章:P5级面试
JVM内存分析排查工具
Java开发人员肯定都知道JDK的bin目录中有java.exe、javac.exe这两个命令行工具,但并非所有程序员都了解过JDK的bin目录下其他各种小工具的作用。随着JDK版本的更迭,这些小工具的数量和功能也在不知不觉地增加与增强。除了编译和运行Java程序外,打包、部署、签名、调试、监控、运维等各种场景都可能会用到它们。
燃192
2023/09/18
1.9K0
JVM内存分析排查工具
jstat使用_jvm常用命令
大家好,又见面了,我是你们的朋友全栈君。 如何判断JVM是否存在内存问题呢?判断JVM垃圾回收是否正常?一般的top指令基本上满足不了这样的需求,因为它主要监控的是总体的系统资源,很难定位到java应用程序。 Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。可见,Jstat是轻量级的、专门针对JVM的工具,非常适用。jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。
全栈程序员站长
2022/10/01
5100
那个小白还没搞懂内存溢出,只能用案例说给他听了
内存溢出,通俗的理解,就是你要求分配的内存超出了JVM能给你的,JVM不能满足需求,于是产生溢出。 为了便于理解,本文我们将使用一个案例来说明内存溢出。
田维常
2020/11/19
6310
那个小白还没搞懂内存溢出,只能用案例说给他听了
JVM参数配置&&命令工具
大致方向:JVM调优的目的是保证在一定吞吐量的情况下尽可能的减少GC次数,从而减少系统停顿时间,提高服务质量和效率。
Noneplus
2019/09/24
1.1K0
JVM参数配置&&命令工具
GC原理介绍、排查FGC及线上故障的步骤
JAVA堆分为新生代(Young Generation)和老年代(Old Generation)( 也就是图中对应的New Generation 和 tenured Generation)用于存储对象实例。
chenchenchen
2022/03/09
4.8K0
GC原理介绍、排查FGC及线上故障的步骤
相关推荐
线上java JVM问题排查
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档