意想不到,这个神奇的bug让我加班到深夜

通往编程高手之路:《深入理解操作系统》 亚达斯密在其经济学巨著《国富论》中提出了“看不见的那只手”的概念,意指市场机制对经济发展的作用,作为普通的个人来说我们在平时的生活中可能很难感受到市场机制所发挥的作用,但这只看不见的手却无时无刻不在发挥着它的作用。 而对于程序员来说,在程序的运行环境中也会有一只看不见的手,这只看不见的手无时无刻不在影响着程序的运行,但你可能对此却浑然不知,对于程序员来说这只看不见的手就是操作系统,这只看不见的手就是本书的主题。 作为程序员我们要知道,操作系统从本质上讲也是一个普通的程序,和我们平时所编写的程序没有什么本质的 阅读详情

给大家分享一个近期解决的线上问题,起因是这样的,近期参与公司的一个项目,工程量很大,代码编写测试过后终于到了紧张的上线时刻。

 

项目上线

上线前照例忐忑不安了一番,因为工程量比较大,预估可能不会很顺利,但还不至于到了祈祷服务器不要出bug的地步,bug对于程序员来说简直是家常便饭,没有bug反而可能会嘀咕半天,这都是职业病,没治。

紧张了一会儿,我屏气凝神,点了上线按钮,那一刻简直就像在点核按钮一样,生怕点下去后服务器会轰的一声炸掉。

结果一切正常。。。

这不对啊,这时博主的职业病又犯了,这么大的改动不会这么顺利吧,内存、CPU、tp99耗时一切正常,这太不正常了吧。

 

说曹操曹操就到

就在博主想为什么没有bug时,bug简直就像听到了我的召唤一样如约而至,博主当时甚至在想为什么梦想中的500万彩票就这么不听话呢?

(bug妹妹:“程序员哥哥,我来啦”;

500万妹妹:“不,程序员哥哥,不要说门,窗户都没有。。。”)

上线最初一切正常,问题就出在了接下来的一段时间里。

在接下来的时间里,tp99耗时就像通货膨胀一样不可遏制的一路上扬,线上收入就像股市一样不可遏制的一路重挫。

这时博主的内心反而踏实了很多,没错,就是这个味儿,还是熟悉的配方还是熟悉的味道,I know it。

废话少说,赶紧回滚,线上恢复正常后接下来就是问题排查了。

 

排查问题

从监控上看,一次请求的处理时间会越来越高,那么显然问题的关键就是定位耗时出现在了哪段代码上。

没有办法,只能一点一点的去找监控了,幸好代码中监控比较丰富,一番梳理后最终锁定在了这样一行代码:



// 监控代码
obj a = b;
// 监控代码

这段代码本质上是在干什么呢?很简单,就是对象的copy,而且在我们的实现中还是浅copy,也就是仅仅copy了一些字段。

从监控看就是这行代码导致了tp99耗时不断上涨。

这。。这怎么可能呢?简简单单的一个对象拷贝竟然会让耗时上涨那么多,而且还是随着时间缓慢上涨,这也太神奇了吧。

当人遇到自己不能理解的问题时通常会归因于外部因素,博主也不能免俗。

接下来就是怀疑人生的时刻。

不会是监控有问题吧,不会是编译器的原因吧,不会是硬件的原因吧,不会是天气的原因的吧,不会是马上就要国庆放假的原因吧,总之不是我的原因。

一番思索后最终理性战胜了自己,哪有那么多原因,在没有其它证据下目前看就是这个对象拷贝导致的。

那么为什么一个简单的对象拷贝会导致CPU消耗越来越高,耗时越涨越多呢?

这里的关键在于意识到这一点,既然随着时间的推移耗时会越来越高,那么很显然是某个全局性质的数据随着请求的处理越堆积越多,而出问题的这个对象使用到了这个全局数据。没错,就是这样,终于要见到曙光啦,哈哈,激动!

这就解释了为什么这个对象随着时间的推移就和美债一样越滚越多,变得越来越庞大了,虽然美国政府可能没打算还美债,但是CPU拷贝越来越多的数据必然导致耗时越来越高。

 

找出bug

既然明确了方向,接下来就有针对性了。

首先去看一下这个对象都有哪些成员变量,对于内置类型像int、bool之类肯定不会有问题,因为这些类型的变量大小是固定的,需要注意的就是这种vector、set之类的容器。

最终经过一番检查后断定问题就是出在了某个vector成员变量上,同时也验证了上述猜想。

 

真相大白

问题是这样的。

这个对象的某个vector成员变量每次在处理请求时都要用另一个对象(假设为对象A)的数据来进行初始化,就像这样:

 

在每次处理一个请求之前,A持有的Data都会被push一些特定的数据。

而系统为了优化内存分配开销,对象A被放到了内存池中,就像这样:

由于对象被放到了内存池,因此对象A是不会被释放的,这就让对象A无形中变为了全局性质的对象

现在,有的同学可能已经发现问题了,那就是,如果对象A在放回内存池后没有清空持有的Data,那么就会导致这样的一个问题,那就是A持有的Data随着每个请求的到来不断的被push数据,这就会导致A持有的Data就像泡沫一样越吹越大,相应的Obj对象持有的vector也会越来越大:

 

在这种情况下拷贝Obj对象必然要拷贝持有的vector,由于vector越来越大,因此消耗在拷贝上的时间也越来越多,使用内存池本意是好的,但由于使用完后忘记清理其保存的旧数据反而造成了内存泄漏

 

经验教训

这次的问题从代码编写角度看是这样的,对象A中Data字段的清理工作没有放在对象A的Clear函数,反而要靠使用者自己清理,由于代码非常复杂极其容易疏漏,博主就在这里踩坑了。。。

因此,总结下来经验教训就是:

  1. 向类中添加新成员时一定要注意其清理工作,使用前一定要确保该成员是崭新的,里面没有之前旧存货。

  2. 代码中添加必要的监控,有利于排查问题

  3. 测试的时间要稍微长一些,否则类似这里的问题不容易暴露

  4. 程序员遇到bug是很正常的,大胆假设,小心求证,每次问题的解决都是能力的提升

查到问题后,修bug、自测、验证、代码提交一气呵成,再次上线就明天了,收工回家。

从下午上线发现问题到问题解决耗时超过6小时,博主到家时,已是满天繁星。

希望本文能帮助大家避开一些坑,关注公众号“码农的荒岛求生”回复“bug”你会看到博主踩过的坑,也希望你能少走弯路。

 

彻底理解操作系统:1,什么是程序? 虽然在这里我们主要关注的是操作系统,但毕竟操作系统的目的就是能更好的运行用户程序,而且该教程主要是站在程序员的角度来讲解操作系统的,作为程序员是绕不开程序这个话题的。因此在深入理解操作系统之前还是有必要来聊一聊程序是怎么一回事。 那究竟是什么是程序呢? 我们引用Wikipedia对计算机程序的定义: A computer program is a collection of instructio... 阅读详情

相关推荐

如何查看进程开启了多少线程

如何查看进程开启了多少线程####概念澄清####背景知识与原理####实验开始####结论####广告时间 :) 为什么要知道某个进程开启了多少线程? 好奇 性能调优 ####概念澄清 这里所说的线程指的是内核级别的线程,不是用户态线程,所谓用户态线程是指不依赖操作系统而是在用户态下实现的执行流调度。 这里的线程个数指的是进程运行时的线程个数,这个数值会随着线程的创建和销毁而...

码农的荒岛求生 5002

JavaScript 如何在所有的浏览器中,均可获取元素当前显示样式

一、代码部分 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> #box{ width: 200px; height: 200px; ...

路虽远,行则必至! 895

进程退出后占用的内存都去哪儿了?

本文是《深入理解操作系统》第四章,从本章开始将开启第一个重要的主题:进程,彻底理解进程对程序员来说是极为重要的,本章就从程序员的角度来讲解到底什么是进程、操作系统是如何实现进程的。本文承接上篇《进程是如何运行的》,以下为本篇目录: 进程自愿退出 操作系统终止进程 进程终止进程 进程退出后占用的内存去哪了? 总结 本节是进程三部曲的最后一篇,在经历了进程创建和进程运行之后,进程迎来终结,是的,任何进程都不会一直运行,当进程终止后,程序不再运行,进程所占用的资源

码农的荒岛求生 3172

程序员如何在春节假期避免加班

点击上方“程序人生”,选择“置顶公众号”第一时间关注程序猿(媛)身边的故事平日奋战在修复BUG前线的IT界精英程序员们马上就要开始新年假期了平时加班忙成狗的你今年过年想好怎么做才能不加班了么?回想2017年那轰动全国的被迫加班莫过于鹿晗和关晓彤公开恋情导致微博服务器瘫痪的事件万万想不到鹿晗表白来的如此突然即使程序员在自己的婚礼上也难逃加班悲剧但是我!不!相!信!我们程序员只有加班的命!所以今年过

程序人生的博客 3465

@程序员,你还在加班Bug 吗?

并非所有的bug都值得改,也可以不改。作为软件开发者,最想做的事情就是给客户建立并发布最完美的产品和功能。但你也知道,软件开发并不容易,每次修改肯定都会引入bug。毕竟,...

CSDN资讯 1322

意想不到,这个神奇Bug 让我加班深夜

作者 |码农的荒岛求生来源 |码农的荒岛求生(ID:escape-it)给大家分享一个近期解决的线上问题,起因是这样的,近期参与公司的一个项目,工程量很大,代码编写测试过后终于到了紧...

程序人生的博客 558

意想不到!这个神奇bug让我加班深夜

意想不到!这个神奇bug让我加班深夜 2021-05-30 16:07:08·马小乎 给大家分享一个近期解决的线上问题,起因是这样的,近期参与公司的一个项目,工程量很大,代码编写测试过后终于到了紧张的上线时刻。 项目上线 上线前照例忐忑不安了一番,因为工程量比较大,预估可能不会很顺利,但还不至于到了祈祷服务器不要出bug的地步,bug对于程序员来说简直是家常便饭,没有bug反而可能会嘀咕半天,这都是职业病,没治。 紧张了一会儿,我屏气凝神,点了上线按钮,那一刻简直就像在点核按钮一样,生怕点下去

maxiao112的博客 169

记一个神奇Bug

多年以后,当Abraham凝视着一行行新时代的代码在屏幕上川流不息的时候,他会想起2019年4月17日那个不平凡夜晚,以及在那个夜晚他发现的那个不可思议的Bug。 虽然像无数个普普通通的夜晚一样,我在公司加班,一样ssh到公司在北京的服务器,但是发现resnet50的计算精度比上一版代码低了1%。一通着急忙慌的Debug之后无济于事,只能求助于Git来比较新旧版本异同。 由于精度降低...

dianhuo7207的博客 206

放弃加班BUG!程序员用DeepSeek竟能躺着完成项目?

结果用DeepSeek的版本适配器,边喝奶茶边点"自动转换"按钮,下班前居然收到了成功编译的通知。最让我感动的是迁移报告里那行小字:"发现3处JPA方法命名不规范,已自动修正"——这哪是工具,分明是请了个不要工资的代码审查员。上次赶着交差时,我试着在控制台输入"生成带熔断机制的Feign客户端",20秒后得到的模板代码不仅整合了Hystrix,连降级策略都预设了三种模式。更离谱的是,当我把同事写的复杂SQL贴进调试区,它居然用JPA的Specification重写了一版,性能直接提升了40%。

weixin_50019283的博客 281

【python实用小脚本-36】深夜加班救星!一行Python代码让你的电脑自动关机

这里我们创建了一个“关机按钮”。os.system是跟电脑对话的传声筒,/s表示“我要关机啦”/t 1表示“1秒后执行”,避免突然袭击这个小小的自动关机脚本,就像给电脑设了个“生物钟”。深夜加班时强制自己准时睡觉下载大文件时设置“完成后自动关机”给家里的熊孩子限制定时游戏时间编程的魅力就在于此:用几行代码,就能把枯燥的重复操作交给电脑,把时间留给自己真正重要的事情。需要完整源码的朋友,欢迎在评论区留言,或者私信我,让我们一起用代码打造更聪明的生活!

kylner的博客 800

彻底理解二叉树的遍历

什么是二叉树 在计算机科学中二叉树,binary tree,是一种数据结构,在该数据结构中每个节点最多有两个子节点,如图所示: 二叉树的定义就是这样简单,但这种看起来很简单的数据结构遍历起来一点都不简单。 如何遍历二叉树 所谓遍历简单的讲就好比在迷宫中寻宝,宝物就藏在某一个树节点当中,但我们并不知道具体在哪个节点上,因此要找到宝物就需要将全部的树节点系统性的搜索一遍。 那么该怎么系统性的搜索一遍...

码农的荒岛求生 2万+

进程切换与线程切换的区别?

注意这个题目问的是进程切换与线程切换的区别,不是进程与线程的区别。当然这里的线程指的是同一个进程中的线程。 这个问题能很好的考察面试者对进程和线程的理解深度,有比较高的区分度。 要想正确回答这个问题,面试者需要理解虚拟内存。   虚拟内存解放生产力 对于程序员来说,我们在编程时实际上是不怎么操心内存问题的,对于使用Java、Python、JavaScript等动态类型语言的程序员来说更是...

码农的荒岛求生 1万+

使用多线程会加快文件读取速度吗?

前几天遇到一个很有意思的问题,提炼出来就是使用多线程去读取文件的话会加快读取速度吗? 在往下看之前先自己想一想,同样如果在面试中问你这个问题该怎么回答呢? 假设有一个文件大小为10G,我们需要将其读取到内存中(假设内存能容纳下该文件),那么使用多个线程分块去读取的话会加快读取速度吗,比如我们创建两个线程,一个线程读取前5G数据;另一个线程读取后5G数据,这会比只是用一个线程将其读入内存快吗? 先说...

码农的荒岛求生 7353

如何在C++和Python间优雅的共享数据

前几天工作中遇到一个需求,C++和Python混合编程,这个刚开始没有多想,内心觉得这应该很简单,无非就是把C++代码暴露出C接口,并且编译成动态链接库的形式供Python加载起来就好了。 有的同学可能会想为什么需要用Python去调用C++代码呢?我们都知道,Python代码是解释执行的,用起来非常方便,但是解释执行的代码都有一个缺点,那就是运行起来非常慢;而C++是编译型语言,性能不是问题,但...

码农的荒岛求生 2492
上一篇: 读取文件时,程序经历了什么?
下一篇: 终于明白了,一文彻底理解I/O多路复用
码农的荒岛求生
博客等级 码龄10年 968粉丝 123原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值