公司花50k挖了一个BAT架构师,写代码上线直接内存溢出,被开除了!

简介: 公司花50k挖了一个BAT架构师,写代码上线直接内存溢出,被开除了!


来源:小姐姐味道


部门新来了个架构师,BAT背景,住在三环,开宝马上班,有车位。

小伙话不多,但一旦说话斩钉截铁,带着无法撼动的自信。原因就是,有他着数亿高并发经验,每一秒钟的请求,都是其他企业运行一年也无法企及的。这就让人非常羡慕,毕竟他靠这个比我赚的钱要多。

俗话说,要想在公司不出事故,那就不要写代码。干活多了容易出事,一身轻松无人问津,这就是现实。

但有时候还是要看成果的。新来的研发领导不懂技术,但他懂技术指标,所以就统计大家提交git的数量,如果git活动是一片绿色如A股,那就算过关了。

架构师思来想去,决定领一个并发量最高的需求 :统计接口的平均响应时间和启动以来的请求数。

为什么说它的并发量高呢?这是因为,它是统计所有接口的,自然比每一个接口的请求量都要大。AOP代码一包,每个接口都得从他这里走一圈。

该我们的架构师上场了。代码如图。

架构师说,我的代码不需要做注释。所谓的注释,都是给垃圾代码用的。我深以为是,他明显是受到了Netflix公司的影响。

程序考虑到了高并发场景,使用了线程安全的ConcurrentHashMap,然后每次通过监控key取出相应的数据,然后在value上递增。这么简单的代码,确实不需要增加什么注释。

作为项目里并发量最高 的代码,出于对高级架构师的信任,我们并不需要做什么代码review,也不需要做什么测试。大家都很忙,代码您呐,到线上遛一遛吧。

我建议你先找一找代码的问题,如果你发现了问题,那就比架构师还厉害;如果你没发现,也不证明你比架构师弱,没有什么好伤心的。

装B遭雷劈,线上运行一段时间后,内存溢出了。

大家吵吵个没完,毕竟xjjdog说过,内存溢出问题的排查周期很长,大约平均需要40天左右才能解决问题。在大家开始论证的时候,架构师偷偷的启动了Eclipse MAT 。MAT用来分析内存问题是非常合适的,但前提是你需要把堆栈给捣鼓下来。

架构师会用jmap,最主要的是权限大,于是自己搞了一份拷贝到线下分析。

我能理解到他的心情,毕竟问题定位到自己的代码不是一件什么值得高兴的事情。他发现内存的堆里面,满满的全是MonitorKeyMonitorValue

Monitor$MonitorKey@15aeb7ab

我和架构师关系比较好,于是他问我:咱们的接口是不是特别的多?

我说:不是啊,你别看访问量大,就这么个狗屁业务能有多少接口?几百个撑了天了。

他说:我在堆里发现了几千万个...

说完他就不言语了,因为他发现里面有不少是一样的接口。一定是参数的原因,所以他在代码里加了这个,把后面的给截断了。

key = key.split("\\?")[0];

结果发布到线上,过不了多久内存又溢出了。这次终于引起了大牛们的注意,经过大家的分析,发现代码是忘了给MonitorKey重写equalshashCode方法了。

我不禁脸红起来。作为好朋友,我不应该让他出这个丑。但我又是隐隐快乐的,因为他工资比我高。

所以这就是一个很大的问题。很多同学对HashMap的知识点对答如流,甚至还专门记忆了红黑树。但换一个方式去问,却又一脸懵逼。

其中一种问法是这样的:一个普通的对象,能够作为HashMap的key么?

答案显然是可以的,但需要注意重写hashCode和equals方法。如果忘记重写的话,大概率会造成内存泄漏。

很不幸,现实中忘记的案例很多。大牛架构师也会中招。

代码重写hashCode和equals方法后,线上就再也没发生过内存溢出。

等等,还没完。毕竟是架构师,仅仅这样一个bug还是证明不了水平的。架构师写的bug,肯定非比寻常。

这种事出现的多了,研发领导对技术的权威性就不再是那么感冒。我们决定从并发量最高的代码开始,进行一下代码review。

很不幸,架构师的visit代码出现问题了。虽然问题不是很大,但它毕竟是个问题。

在统计数据的时候,代码使用了ConcurrentHashMap,但它并没有什么卵用。

visit方法,首先拿出了key,然后判空,再塞值。这明显不是一个原子操作。

线程1:获取key为a的值
线程2:获取key为a的值
线程1:a为null,生成一个b
线程2:a为null,生成一个c
线程1:保存a=b
线程2:保存a=c

此时,B丢了。

业务可以忍受,但严谨的技术大牛们忍受不了,提出了修改的意见。

架构师说,给visit方法加个synchronized不就成了。

public synchronized void visit(String url, String desc, long timeCost) 

我说不行。有更优雅的写法,效率更高。那就是使用putIfAbsent方法,代码改动如下:

MonitorKey key = new MonitorKey(url, desc);
MonitorValue value = monitors.putIfAbsent(key, new MonitorValue());
value.count.getAndIncrement();
value.totalTime.getAndAdd(timeCost);
value.avgTime = value.totalTime.get() / value.count.get();

大家就这两种方式争论了起来。

技术总监托着腮想了半天,看了看争的面红耳赤的同学们,说:这就是我不放心你们的缘故。线上环境要尽量保持稳定性,做最小的变更。既然加个synchronized就能够很容易简单解决的问题,为啥不直接用呢?下面这种代码改动太大,有风险。

总监接着把头转向我:这个BUG非比寻常,为了让大家引以为戒,你来做整个事故的复盘。把问题的排查和得到的教训分享给大家,让大家向这种至简的架构看齐。我们平常的工作中,也要尽量以结果导向为主,用什么手段无所谓,能漂亮把事情办好就行

这就是此篇文章的由来,我虚心受教,同时也明白自己的工资是涨不上去了。

image.png

相关文章
|
1月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用合集之idea本地测试代码,要增大 Flink CDC 在本地 IDEA 测试环境中的内存大小如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
存储 C++
webserver--Buffer类实现内存缓冲区读写(全代码)
webserver--Buffer类实现内存缓冲区读写(全代码)
|
14天前
|
前端开发 JavaScript 安全
微前端架构采用 TypeScript 提升开发效率和代码可靠性
【6月更文挑战第12天】微前端架构采用 TypeScript 提升开发效率和代码可靠性。TypeScript 的类型安全防止了微前端间的类型错误,智能提示与自动补全加速开发,重构支持简化代码更新。通过定义公共接口和使用 TypeScript 编写微前端,确保通信一致性与代码质量。在构建流程中集成 TypeScript,保证构建正确性。总之,TypeScript 在微前端架构中扮演关键角色,推荐用于大型前端项目。
42 4
|
1月前
|
机器学习/深度学习 存储 并行计算
深入解析xLSTM:LSTM架构的演进及PyTorch代码实现详解
xLSTM的新闻大家可能前几天都已经看过了,原作者提出更强的xLSTM,可以将LSTM扩展到数十亿参数规模,我们今天就来将其与原始的lstm进行一个详细的对比,然后再使用Pytorch实现一个简单的xLSTM。
67 2
|
19天前
|
缓存 关系型数据库 MySQL
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
35 3
|
23天前
|
监控 Rust 安全
Rust代码在公司电脑监控软件中的内存安全监控
使用 Rust 语言开发的内存安全监控软件在企业中日益重要,尤其对于高安全稳定性的系统。文中展示了如何用 Rust 监控内存使用:通过获取向量长度和内存大小来防止泄漏和溢出。此外,代码示例还演示了利用 reqwest 库自动将监控数据提交至公司网站进行实时分析,以保证系统的稳定和安全。
87 2
|
2天前
|
存储 Java
JavaSE 面向对象程序设计初级 静态static 包package 常量final 代码块 代码实操理论内存原理详解
JavaSE 面向对象程序设计初级 静态static 包package 常量final 代码块 代码实操理论内存原理详解
6 0
|
28天前
|
移动开发 监控 Android开发
构建高效Android应用:从内存优化到电池寿命代码之美:从功能实现到艺术创作
【5月更文挑战第28天】 在移动开发领域,特别是针对Android系统,性能优化始终是关键议题之一。本文深入探讨了如何通过细致的内存管理和电池使用策略,提升Android应用的运行效率和用户体验。文章不仅涵盖了现代Android设备上常见的内存泄漏问题,还提出了有效的解决方案,包括代码级优化和使用工具进行诊断。同时,文中也详细阐述了如何通过减少不必要的后台服务、合理管理设备唤醒锁以及优化网络调用等手段延长应用的电池续航时间。这些方法和技术旨在帮助开发者构建更加健壮、高效的Android应用程序。
|
26天前
|
Cloud Native 算法 程序员
代码与禅意:编程中的哲学思考构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第30天】 在数字世界的繁花似锦之下,编程不仅仅是一种技能,更是一场关于逻辑、美学和哲学的深刻对话。本文将探讨编程过程中所体现出的哲学理念,从禅宗的角度出发,揭示代码背后蕴含的深层次意义。我们将一同走进程序员的内心世界,体会在面对复杂问题时,如何通过冥想般的编码实践,达到问题解决的顿悟。
|
1月前
|
架构师 持续交付 开发者
代码之禅:从模块化到架构的艺术
【5月更文挑战第26天】 在软件开发的不断进化中,技术栈的深化与技术的模块化构建始终是提升项目可维护性与扩展性的核心。本文将探讨如何通过细致的模块化设计和系统架构思考,实现从代码编写细节到整体架构布局的升华。我们将透过实战案例,深入剖析模块化的重要性,以及它如何影响系统的可维护性、扩展性和性能优化。