[转帖]Cat导致内存不足原因分析
背景
线上几亿的数据在回刷的时候容器服务会出现OOM而重启,导致任务中断
内存泄露分析
jmap -histo pid 找出了有几十亿的java.lang.StackTraceElement对象,找不到被谁引用了
jmap -dump:format=b,file=heapdump.hprof pid dump内存
下载到本机mac上,用mat(MemoryAnalyzer)分析,得到内存泄露报告,看到内存全部被com.dianping.cat.message.internal.DefaultMessageManager$Context引用,找到了罪魁祸首

原因分析
com.dianping.cat.log4j.Log4j2Appender 在打印错误Exception的时候会调用Cat.logError方法
public class Log4j2Appender extends AbstractAppender {
....
public void append(LogEvent event) {
try {
Level level = event.getLevel();
if (level.isMoreSpecificThan(Level.WARN)) {
this.logError(event);
}
} catch (Exception var3) {
if (!this.ignoreExceptions()) {
throw new AppenderLoggingException(var3);
}
}
}
....
private void logError(LogEvent event) {
Throwable exception = event.getThrown();
if (exception != null) {
Message message = event.getMessage();
if (message != null) {
Cat.logError(message.getFormattedMessage(), exception);
} else {
Cat.logError(exception);
}
}
<span class="token punctuation">}</span>
}
Cat类的logError函数最终调用到了DefaultMessageManager.shouldLog方法
public class Cat {
...
public static void logError(String message, Throwable cause) {
try {
getProducer().logError(message, cause);
} catch (Exception var3) {
errorHandler(var3);
}
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">logError</span><span class="token punctuation">(</span><span class="token class-name">Throwable</span> cause<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">getProducer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">logError</span><span class="token punctuation">(</span>cause<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> var2<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">errorHandler</span><span class="token punctuation">(</span>var2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
}
public class DefaultMessageProducer implements MessageProducer {
public void logError(String message, Throwable cause) {
if (Cat.getManager().isCatEnabled()) {
if (this.shouldLog(cause)) {
....
}
} else {
cause.printStackTrace();
}
}
private boolean shouldLog(Throwable e) {
return this.m_manager instanceof DefaultMessageManager ? ((DefaultMessageManager)this.m_manager).shouldLog(e) : true;
}
DefaultMessageManager类的m_context在shouldLog的时候把异常堆栈保存下来了,如果Cat事务不关闭,随着异常越来越多就导致了内存溢出
public class DefaultMessageManager {
private ThreadLocal<DefaultMessageManager.Context> m_context = new ThreadLocal();
boolean shouldLog(Throwable e) {
DefaultMessageManager.Context ctx = (DefaultMessageManager.Context)this.m_context.get();
return ctx != null ? ctx.shouldLog(e) : true;
}
<span class="token keyword">class</span> <span class="token class-name">Context</span> <span class="token punctuation">{<!-- --></span>
<span class="token comment">// 内存不足就是由于错误堆栈信息没有限制导致的</span>
<span class="token keyword">private</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Throwable</span><span class="token punctuation">></span></span> m_knownExceptions<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token class-name">Context</span><span class="token punctuation">(</span><span class="token class-name">String</span> domain<span class="token punctuation">,</span> <span class="token class-name">String</span> hostName<span class="token punctuation">,</span> <span class="token class-name">String</span> ipAddress<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>m_knownExceptions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">shouldLog</span><span class="token punctuation">(</span><span class="token class-name">Throwable</span> e<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>m_knownExceptions <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">this</span><span class="token punctuation">.</span>m_knownExceptions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>m_knownExceptions<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
<span class="token comment">// 这里没有限制大小,只要有异常就往Set里面添加,这里应该做一个优化</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>m_knownExceptions<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
}
}
解决方案
手动调用Cat.getManager().reset();方法清空保存的异常堆栈信息
public void reset() {
DefaultMessageManager.Context ctx = (DefaultMessageManager.Context)this.m_context.get();
if (ctx != null) {
if (ctx.m_totalDurationInMicros == 0L) {
ctx.m_stack.clear();
ctx.m_knownExceptions.clear();
this.m_context.remove();
} else {
// 这里会释放错误日志堆栈信息
ctx.m_knownExceptions.clear();
}
}
}
[转帖]Cat导致内存不足原因分析的更多相关文章
- tomcat 内存溢出原因分析及解决
一.错误提示:java.lang.OutOfMemoryError: Java heap space [原因分析] tomcat默认可以使用内存为128MB,在较大型的应用项目中不足以满足运行要求,在 ...
- Android 非静态内部类导致内存泄漏原因深入剖析
背景 上周发现蘑菇街IM-Android代码里面.一些地方代码编写不当.存在内存泄漏的问题.在和疯紫交流的过程中.发现加深了一些理解,所以决定写一下分析思路,相互学习. 内存泄漏 一个不会被使用的对象 ...
- Activity内部Handler引起内存泄露的原因分析
有时在Activity中使用Handler时会提示一个内存泄漏的警告,代码通常如下: public class MainActivity extends Activity { private Text ...
- Spark集群无法停止的原因分析和解决
今天想停止spark集群,发现执行stop-all.sh的时候spark的相关进程都无法停止.提示: no org.apache.spark.deploy.master.Master to stop ...
- drawRect导致内存暴增的真正原因
那么现在我们分析一下drawRect导致内存暴增的真正原因: 重写drawRect为何会导致内存大量上涨? 要想搞明白这个问题,我们需要撸一撸在 iOS 程序上图形显示的原理.在 iOS 系统中所有显 ...
- iOS学习——内存泄漏检查及原因分析
项目的代码很多,前两天老大突然跟我说项目中某一个ViewController的dealloc()方法没有被调用,存在内存泄漏问题,需要排查原因,解决内存泄漏问题.由于刚加入项目组不久,对出问题的模块的 ...
- .Net线程池ThreadPool导致内存高的问题分析
最近写了一个WinFrom程序.此程序侦听TCP端口,接受消息处理,然后再把处理后的消息,利用线程池通过WebService发送出去(即一进一出). 在程序编写完成后,进行压力测试.用Fiddler提 ...
- 关于JVM内存溢出的原因分析及解决方案探讨
前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1. ...
- ThreadLocal内存溢出代码演示和原因分析!
ThreadLocal 翻译成中文是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能操作自己的私有变量,所以不会造成线程不安全的问题. 线程不安全是指,多个线程在同一时刻对同一个全局 ...
- 添加IFrame导致内存溢出的解决过程(IE浏览器,目前发现了原因,还未解决)
1. 现象 每次动态添加iframe时,iexplore.exe进程占据的内存都会增加(大概10M左右),不会自动释放,最终导致内存溢出 2. 解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...
随机推荐
- Karmada 结合 coreDNS 插件实现跨集群统一域名访问
本文分享自华为云社区<Karmada 结合 coreDNS 插件实现跨集群统一域名访问>,作者:云容器大未来 . 在多云与混合云越来越成为企业标配的今天,服务的部署和访问往往不在一个 K8 ...
- Angular:都2021年了,你为啥还没用Angular
摘要:数据绑定是将应用程序UI或用户界面绑定到模型的机制.使用数据绑定,用户将能够使用浏览器来操纵网站上存在的元素. Web开发需要模型和视图之间的数据同步.这些模型基本上包含数据值,而视图则处理用户 ...
- 2023开发者必备iOS开发工具
2023开发者必备iOS开发工具 工欲善其事,必先利其器.进行开发工作时,利用并熟练使用恰当的工具可以让工作效率得到大幅度提高.下边会介绍一些在进行iOS开发工作时常用的一些工具,本文并不对其进行 ...
- 火山引擎DataLeap基于Apache Atlas自研异步消息处理框架
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 字节数据中台DataLeap的Data Catalog系统通过接收MQ中的近实时消息来同步部分元数据.Apache ...
- 火山引擎数智平台 VeDI 帮助智能投影仪更懂用户需求
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 当露营成为年轻人的一种全新生活方式后,连带着户外野营帐篷.可折叠桌椅.卡式炉.多人趣味桌游等露营周边市场都迎来新一 ...
- Solon2 开发之IoC,四、注入依赖与初始化
Solon 强调 有克制的注入 + 手动控制 结合的模式.好处是,代码用料少.启动快. Bean 的关键生命节点: 节点 说明 1. Constructor(构造方法) 不支持参数注入 2. @Inj ...
- 24校招,Moka测试开发工程师一面
前言 大家好,今天回顾一下楼主当时参加moka测试开发工程师的面试 对其中一些重要问题,我也给出了相应的答案 过程 自我介绍 挑一个项目,详细介绍你在其中担任的职责 如何安排工作的,有什么成果? 回归 ...
- vivo 悟空活动中台 - 栅格布局方案
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/6O0CH0U_WE1YkPK75m-jDQ作者:悟空中台研发团队 一.背景 今天来给大家分享 ...
- SpringBoot-mybatisplus-模糊查询
模糊查询如何实现如下案例中两种实现方法 第一种:利用QueryWrapper.like自己实现. 第二种:使用@TableField(condition = SqlCondition.LIKE)实现. ...
- location对象的方法
location.assign() 跟href一样,可以跳转页面(也称为重定向页面). location.replace() 替换当前页面,因为不记录历史,所以不能后退页面. location.rel ...