背景

线上几亿的数据在回刷的时候容器服务会出现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">&lt;</span><span class="token class-name">Throwable</span><span class="token punctuation">&gt;</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导致内存不足原因分析的更多相关文章

  1. tomcat 内存溢出原因分析及解决

    一.错误提示:java.lang.OutOfMemoryError: Java heap space [原因分析] tomcat默认可以使用内存为128MB,在较大型的应用项目中不足以满足运行要求,在 ...

  2. Android 非静态内部类导致内存泄漏原因深入剖析

    背景 上周发现蘑菇街IM-Android代码里面.一些地方代码编写不当.存在内存泄漏的问题.在和疯紫交流的过程中.发现加深了一些理解,所以决定写一下分析思路,相互学习. 内存泄漏 一个不会被使用的对象 ...

  3. Activity内部Handler引起内存泄露的原因分析

    有时在Activity中使用Handler时会提示一个内存泄漏的警告,代码通常如下: public class MainActivity extends Activity { private Text ...

  4. Spark集群无法停止的原因分析和解决

    今天想停止spark集群,发现执行stop-all.sh的时候spark的相关进程都无法停止.提示: no org.apache.spark.deploy.master.Master to stop ...

  5. drawRect导致内存暴增的真正原因

    那么现在我们分析一下drawRect导致内存暴增的真正原因: 重写drawRect为何会导致内存大量上涨? 要想搞明白这个问题,我们需要撸一撸在 iOS 程序上图形显示的原理.在 iOS 系统中所有显 ...

  6. iOS学习——内存泄漏检查及原因分析

    项目的代码很多,前两天老大突然跟我说项目中某一个ViewController的dealloc()方法没有被调用,存在内存泄漏问题,需要排查原因,解决内存泄漏问题.由于刚加入项目组不久,对出问题的模块的 ...

  7. .Net线程池ThreadPool导致内存高的问题分析

    最近写了一个WinFrom程序.此程序侦听TCP端口,接受消息处理,然后再把处理后的消息,利用线程池通过WebService发送出去(即一进一出). 在程序编写完成后,进行压力测试.用Fiddler提 ...

  8. 关于JVM内存溢出的原因分析及解决方案探讨

    前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1. ...

  9. ThreadLocal内存溢出代码演示和原因分析!

    ThreadLocal 翻译成中文是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能操作自己的私有变量,所以不会造成线程不安全的问题. ​ 线程不安全是指,多个线程在同一时刻对同一个全局 ...

  10. 添加IFrame导致内存溢出的解决过程(IE浏览器,目前发现了原因,还未解决)

    1.  现象 每次动态添加iframe时,iexplore.exe进程占据的内存都会增加(大概10M左右),不会自动释放,最终导致内存溢出 2.  解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...

随机推荐

  1. antd ui的from使用问题

    select 的allowClear失效问题 select的value与allowClear同时使用会导致allowClear失效 解决方法 from包装一层,通过const [form] = For ...

  2. 1024 | 9位开发者分享生涯“最”时刻,文武状元大PK等你来

    本文分享自华为云社区<1024程序员节,和华为云一起做不被定义的开发者>,作者:华为云社区精选 . 1024,祝所有开发者们节日快乐 "代码有注释,程序无bug, 需求不改动,永 ...

  3. 云小课 | 不了解EIP带宽计费规则?看这里!

    摘要:带宽要变更, 费用不会算?要问怎么办,小课带你看! 和小课一起来学习弹性公网IP(EIP)带宽的计费方式.使用场景及变更影响吧~ 在使用弹性公网IP(EIP)产品后,你是否遇到过以下几种场景: ...

  4. WeLink的杀手锏和远程办公软件的另一面

    摘要:看WeLink如何从内到外,为广大企业用户带来数字化办公转型上的突破? 本文分享自华为云社区<[大厂内参]第11期:WeLink的杀手锏和远程办公软件的另一面>,作者: 华为云社区精 ...

  5. Error: Could not find or load main class org.elasticsearch.tools.java_version_checker.JavaVersionChecker

    把elasticsearch目录换到不属于root目录的其他目录就行了

  6. 使用appuploader工具流程(Windows版本)

    ​ 转载:使用appuploader工具流程(Windows版本) 目录 转载:使用appuploader工具流程(Windows版本) 一.登录apple官网,注册账号 二.下载Appuploade ...

  7. SQL Server 2016 自动备份、收缩数据库

    一般策略为:Oracle: 周一.二 增量备份,周三差异备份,周四.五.六增量备份,周日完整备份(建多计划) MsSQL: 周一.六差异备份,周日完整备份(建多计划) 打开SQL server配置管理 ...

  8. ThreadPoolExecutor 介绍

    线程池能够带来3个好处: 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗:提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行:提高线程的可管理性:线程是稀缺资源,如 ...

  9. Mac下Homebrew替换镜像

    替换git和homebrew镜像源 一.git加速 查找域名对应的地址 nslookup github.com nslookup github.global.ssl.fastly.Net 修改host ...

  10. CNCF大使预测:2024年云原生面临倦怠、离职及云成本精简

    本文由 CNCF 大使 Eric D. Schabell 撰写,预测2024年云原生领域最可能发生的3大变化,并与其对云原生可观测性领域的见解结合. 关注云原生倦怠 毫无疑问,在 2023 年中云原生 ...