- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章高并发服务优化篇:详解一次由读写锁引起的内存泄漏由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
JVM相关的异常,一直是一线研发比较头疼的问题。因为对于业务代码,JVM的运行基本算是黑盒,当异常发生时,较难直观的看到和找到问题所在,这也是我们一直要研究其内部逻辑的原因.
本篇就由一个近期线上JVM内存泄漏的例子,带大家强行分析一波~ 。
Part1 线上服务器报警了 。
某天,同事来找我帮忙,原来是某系统毫无征兆的来了一连串报警,一波机器的老年代内存占用率超过阈值~ 。
1.1先看表现 。
老年代内存占用 。
可以看到,在7月中旬之前,内存占用还是比较正常的,每次GC都可以回收掉很大一部分的老年代对象.
而中旬之后,老年代内存一直缓慢增长而无法释放。很明显,应该是对象没法被正常回收导致.
内存泄漏了~ 。
1.2 怎么办呢 。
如果是刚上线的项目爆出了此类问题,因为影响面比较小,可以直接先回滚代码,止血为第一要务.
不过,这个项目明显已经上线N多天,中间还不知道上过多少需求,而且,既然流量近期有上涨导致问题出现,说明,已经对客开流量了.
回滚是不可能了,抓紧时间定位问题,上线修复吧.
Part2 定位问题 。
一般的步骤:
不过,因为这次dump下来的文件十多G,太大的,MAT基本无能为力,只能打印出来人工分析了 。
2.1 定位问题代码 。
jmap结果查看 。
很幸运,异常对象非常明显。Point对象和GeoDispLocal对象,居然多达好几百万实例数,那就先看下代码中这两个对象是怎么用的.
都是被存放在本次缓存CacheMap中(内存泄漏的一个常见原因,就是因为被静态集合持有,无法回收导致),而dump文件中的CacheMap.Entry也是非常高的.
CacheMap就是我们的第一优先怀疑对象了。先看下这个缓存类是怎么回事:
内部依赖一个带LRU功能的map,怎么实现的呢
内部是一个依赖LinkedHashMap实现的LRU缓存。看注释,目的是要构建一个限定容量、且不会进行扩容的MAP(百度了一波,和网上的实现一模一样~)。那么,实际情况真的和想象中的一样么?.
2.2 LinkedHashMap实现的LRUMap好使么 。
我们来看容量和扩容相关的设置:为什么设计者认为该LRUMap不会进行扩容?
按我们的实际使用实例化一下:
因为复写了LRU条件函数,当size>6000时会进行LRU替换。因此,理论上,size永远不会达到8110.
怎么解决并发下的读写冲突呢?
设计者为了解决并发下的读写冲突,给查询和修改方法加了锁,为了兼顾性能,使用了读写锁:在get的时候加读锁,在put/remove的时候加写锁.
看起来,整个设计很好的解决了LRUMap的固定容量和并发操作问题,那么事实是什么样的呢?
其实,这个问题很早就有人分析过了[1] ,是因为LinkedHashMap在get读操作的时候,会为了维护LRU从而进行元素修改,即将get到的元素转移到链表最后。这样,就导致了读写并发问题,但这个解释感觉朦朦胧胧,因此,我决定在其基础上对读写并发问题再讲细致一些.
2.3 LinkedHashMap内存泄漏拆解 。
都加了读写锁为什么不好使呢?
这里我们还是需要先明确,读写锁的概念和适用场景:读写锁,允许多个线程共享读锁,适用于读多写少的情况。(前提是,读操作不会改变存储结构) 。
所以,问题就发生在get操作上,LinkedHashMap的get操作被重写,目的是为了实现LRU功能,在get之后,将当前节点移动到链表最后.
移动啊,同志们,这明显是一个写操作,所以,加读锁还有用么?
即允许多线程进入,又进行了修改,那还能起什么作用,能没有并发问题么?
下面,对照节点移动的代码,详细拆解一下多线程下的并发问题:
get之后的节点移动,将节点移动到最后 。
实际拆解分析如下,为什么在多线程的情况下,会出现内存泄漏:
时间片下多线程的get执行 。
我们看到,在线程1执行完前两句,让出了时间片,当线程2执行到p.after=null之后又出让了时间片,这样,本来a应该是后面的<2,B>节点,结果多线程下变成了null,最终,后面两个节点被踢出了链表,删除操作无法触达,造成内存泄漏.
验证的代码就不贴了,大家有兴趣可以自己试一下~ 。
Part3 总结 。
话说回来,既然定位到了问题,这个内存泄漏怎么修复呢?
可以把读写锁改成互斥锁。或者直接用分布式存储,能慢多少呢,是不是,既方便,简单,又免得为了节约机器内存自己构造LRUMap.
每一个八股文都不只是为了面试,而是每次线上问题排查的基石。千万别把八股文的作用定位错了。。.
原文链接:https://mp.weixin.qq.com/s/UPCIgL0_SLyOF5SNFgL27w 。
最后此篇关于高并发服务优化篇:详解一次由读写锁引起的内存泄漏的文章就讲到这里了,如果你想了解更多关于高并发服务优化篇:详解一次由读写锁引起的内存泄漏的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试在多线程环境中实现某种累积逻辑;我想知道没有 lock 和 synchronized 关键字是否有更好/更快的方法来做到这一点?以下是我当前的代码: public class Concurr
我需要帮助构建一个实现信号量的监视器,简单的 C 示例就可以。 这是为了证明可以在任何可以使用信号量的地方使用监视器。 最佳答案 如果您说允许使用互斥锁/condvars,请检查: #include
我已经构建了一些返回部分产品目录的 ajax,并且我正在尝试将 xml 输出到文档中,到目前为止,这是我所拥有的: $("#catalog").append("Item NamePriceDe
很抱歉,如果我的问题之前已经被问过,或者它太明显了,但我真的需要澄清这一点。感谢您的帮助。 在多用户界面中,如果来自不同用户的相同事务同时到达服务器,会发生什么? 我有下一张表: create tab
这可能是一个愚蠢的问题,但是这个程序的输出(它的方式)可以为零吗? public class Test2{ int a = 0; AtomicInteger b = new Atomi
假设我本地主机上的一个网站处理每个请求大约需要 3 秒。这很好,正如预期的那样(因为它在幕后进行了一些奇特的网络)。 但是,如果我在选项卡(在 firefox 中)中打开相同的 url,然后同时重新加
我对 MongoDB 的读锁定有点困惑。单个集合可以支持多少个并发读取操作? 最佳答案 如 tk 给出的链接中所写:http://www.mongodb.org/pages/viewpage.acti
如果有四个并发的 CUDA 应用程序在一个 GPU 中竞争资源会发生什么这样他们就可以将工作卸载到图形卡上了? Cuda Programming Guide 3.1 提到那里 某些方法是异步的: 内核
👊上次的百度面试遇到了关于spark的并发数的问题,今天我们就来将这些问题都一并解决一下,图画的的有点丑,还行大家见谅,百度实习的问题我放在了下面的链接👇: 链接: 2022百度大数据开发工程师实
我对 Groovy 线程有疑问。 我的任务是以某种方式翻译给定目录中的每个文件 并将生成的输出放在其他目录中的文件中。 我编写了以下代码,该代码有效: static def translateDir(
Java中的同步和锁定有什么区别? 最佳答案 synchronized是语言关键字;锁是对象。 当一个方法或代码块被标记为同步时,您是说该方法或代码块必须先获得某个锁对象(可以在同步的语法中指定)才能
我需要创建一个能够同时处理来自客户端的多个请求的并发 RPC 服务器。 使用 rpcgen linux编译器(基于sun RPC),不支持-A为并发服务器创建 stub 的选项。 (-A 选项在 so
System.out.println("Enter the number of what you would like to do"); System.out.println("1 = Manuall
我正在将我的应用程序移植到 iOS 8.0 并注意到 UIAlertView 已被弃用。 所以我改变了使用 UIAlertController 的方法。这在大多数情况下都有效。 除了,当我的应用程序打
我正在逐行同时读取两个文本文件。 我特别想做的是当lineCount在每个线程上都是相同的我想看看扫描仪当前正在读取的字符串。 我环顾四周寻找可以实现的某些模式,例如 Compare and Swap
我正在阅读 Java Concurrency in Practice .在章节中断政策部分 取消和关闭 它提到 A task should not assume anything about the
我正在尝试学习线程,互斥等的基础知识。遵循here的文档和示例。在下面的代码中,我得到预期的输出。问题: 想确认我是否有任何陷阱?我们如何改善下面的代码? 我的线程在哪一行尝试获取互斥锁或正在等待互斥
并发是指两个任务在不同的线程上并行运行。但是,异步方法并行运行,但在同一个线程上。这是如何实现的?另外,并行性怎么样? 这三个概念有什么区别? 最佳答案 并发和并行实际上与您正确推测的原理相同,两者都
以此ConcurrentDouble类定义为例: public class ConcurrentDouble { public double num = 0; public void subt
在得知并发确实增加了许多人的吞吐量后,我一直计划在项目中使用并发。现在我在多线程或并发方面还没有做太多工作,因此决定在实际项目中使用它之前学习并进行简单的概念验证。 以下是我尝试过的两个示例: 1.
我是一名优秀的程序员,十分优秀!