[转帖]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. 解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...
随机推荐
- Kubernetes的拐点助推器:左手开源,右手边缘计算
摘要:KubeEdge 是首个基于 Kubernetes 扩展的,提供云边协同能力的开放式智能边缘计算平台,也是 CNCF 在智能边缘领域的首个正式项目.依托 Kubernetes 强大的容器编排和调 ...
- GaussDB技术解读系列之应用无损透明(ALT)
本文作者 :华为云GaussDB研发高级工程师 藏琦 1.背景 GaussDB作为一款企业级分布式数据库,提供了"同城跨AZ双活.两地三中心.双集群强一致"等极致的高可用容灾能力. ...
- 华为发布5GtoB核心网建设白皮书
摘要:近日,华为发布<5GtoB核心网建设白皮书>. 近日,华为发布<5GtoB核心网建设白皮书>.该白皮书从5GtoB市场特征和发展规律角度切入,深度剖析了产业需求和与之对应 ...
- CANN开发实践:4个DVPP内存问题的典型案例解读
摘要:由于DVPP媒体数据处理功能对存放输入.输出数据的内存有更高的要求(例如,内存首地址128字节对齐),因此需调用专用的内存申请接口,那么本期就分享几个关于DVPP内存问题的典型案例,并给出原因分 ...
- 一文带你了解华为云GaussDB的五大黑科技
摘要:在华为开发者大会2021(HDC·Cloud)中,华为云数据库多位技术专家分享了GaussDB系列数据库全新5大黑科技. 4月24-26日,华为开发者大会2021(HDC·Cloud)在深圳隆重 ...
- AI贺新年,开发者的虎年这样过才有意思
摘要:祝所有的开发者们新春快乐,万事如意迎新年,如虎添翼旺全年! 普通人拜年,发一个祝福微信.程序员拜年,运行一串代码,制作独一无二的拜年短视频. 普通人送祝福,新年快乐.程序员送祝福,信手捏来一首拜 ...
- 助力企业数据飞轮转起来!火山引擎云原生数仓ByteHouse全面大促中
用数据驱动业务提升,早已成为各类企业打破增长瓶颈,推动转型升级的一致共识.在大环境持续推动数字化转型的政策扶持下,企业仍然需要一个可借鉴的成熟经验模式,来彻底理解和实践数据驱动到底应该"怎么 ...
- Java实现压缩文件浅谈
背景: 在Java中,可以使用java.util.zip包提供的类来进行文件的压缩和解压缩操作.主要涉及的类有ZipOutputStream.ZipEntry.ZipInputStream和Infla ...
- Codeforece : 1360C. Similar Pairs(水题)
https://codeforces.com/contest/1360/problem/C We call two numbers xx and yy similar if they have the ...
- AtCoder Regular Contest 120 AB题
比赛链接:Here A - Max Add 观察一下发现每次输出与两点有关,前缀和和当前位置最大值 int main() { cin.tie(nullptr)->sync_with_stdio( ...