Log4j线程死锁–案例研究

此案例研究描述了影响Weblogic Portal 10.0生产环境的Apache Log4j线程争用问题的完整根本原因分析和解决方案。 它还将说明在开发和支持Java EE应用程序时适当的Java类加载器知识的重要性。

本文也是您提高线程转储分析技能和了解线程竞争条件的另一个机会。

环境规格

  • Java EE服务器:Oracle Weblogic Portal 10.0
  • 操作系统:Solaris 10
  • JDK:Oracle / Sun HotSpot JVM 1.5
  • 记录API:Apache Log4j 1.2.15
  • RDBMS:Oracle 10g
  • 平台类型:Web门户

故障排除工具

  • Quest Foglight for Java(监视和警报)
  • Java VM线程转储(线程竞争分析)

问题概述

从我们的Weblogic Portal生产环境之一观察到主要性能下降。 Foglight代理还发送了警报,表明Weblogic线程利用率显着上升,达到默认上限400。

事实的收集和验证

像往常一样,Java EE问题调查需要收集技术事实和非技术事实,因此我们可以得出其他事实和/或就根本原因进行结论。 在采取纠正措施之前,要对以下事实进行核实,以便得出根本原因:

  • 对客户有什么影响?
  • 受影响平台的最近更改? 是的,最近进行的部署涉及少量内容更改以及一些Java库更改和重构。
  • 受影响平台最近的流量增加了吗? 没有
  • 自从多久以来观察到此问题? 部署后观察到新问题
  • 重新启动Weblogic服务器是否可以解决问题? 否,任何重新启动尝试均会立即导致线程激增
  • 回滚部署更改是否解决了问题? 是

结论1:问题似乎与最近的变化有关。 但是,团队最初无法查明根本原因。 现在,我们将在本文的其余部分进行讨论。

Weblogic占用线程报告

最初的线程激增问题由Foglight报告。 如下所示,线程利用率很高(最多400个),从而导致大量待处理的客户端请求,最终导致严重的性能下降。

通常,线程问题需要进行适当的线程转储分析,以查明线程争用的根源。 缺乏这种关键的分析技能将使您无法进行根本原因分析。

对于我们的案例研究,使用简单的Solaris OS命令kill -3 <Java PID>从Weblogic服务器生成了一些线程转储快照。 然后从Weblogic标准输出日志文件中提取线程转储数据。

线程转储分析

分析的第一步是对所有卡住的线程进行快速扫描,并找出问题的“模式”。 我们发现有250个线程卡在以下执行路径中:

"[ACTIVE] ExecuteThread: '20' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=0x03c4fc38 nid=0xe6 waiting for monitor entry [0x3f99e000..0x3f99f970]at org.apache.log4j.Category.callAppenders(Category.java:186)- waiting to lock <0x8b3c4c68> (a org.apache.log4j.spi.RootCategory)at org.apache.log4j.Category.forcedLog(Category.java:372)at org.apache.log4j.Category.log(Category.java:864)at org.apache.commons.logging.impl.Log4JLogger.debug(Log4JLogger.java:110)at org.apache.beehive.netui.util.logging.Logger.debug(Logger.java:119)at org.apache.beehive.netui.pageflow.DefaultPageFlowEventReporter.beginPageRequest(DefaultPageFlowEventReporter.java:164)at com.bea.wlw.netui.pageflow.internal.WeblogicPageFlowEventReporter.beginPageRequest(WeblogicPageFlowEventReporter.java:248)at org.apache.beehive.netui.pageflow.PageFlowPageFilter.doFilter(PageFlowPageFilter.java:154)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at com.bea.p13n.servlets.PortalServletFilter.doFilter(PortalServletFilter.java:336)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at weblogic.servlet.internal.RequestDispatcherImpl.invokeServlet(RequestDispatcherImpl.java:526)at weblogic.servlet.internal.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:261)at <App>.AppRedirectFilter.doFilter(RedirectFilter.java:83)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at <App>.AppServletFilter.doFilter(PortalServletFilter.java:336)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3393)at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)at weblogic.security.service.SecurityManager.runAs(Unknown Source)at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2140)at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2046)at weblogic.servlet.internal.ServletRequestImpl.run(Unknown Source)at weblogic.work.ExecuteThread.execute(ExecuteThread.java:200)at weblogic.work.ExecuteThread.run(ExecuteThread.java:172)

如您所见,当试图将调试信息记录到配置的附加程序和日志文件中时,似乎所有线程都在等待获取Apache Log4j对象监视器(org.apache.log4j.spi.RootCategory)上的锁。 我们如何从此线程堆栈跟踪中找出答案? 让我们剖析此线程堆栈跟踪,以便您更好地了解此线程竞争条件,例如250个试图同时获取同一对象监视器的线程。

此时的主要问题是为什么我们突然看到这个问题? 经过适当验证后,此时还排除了日志记录级别或负载的增加。 回滚以前的更改确实解决了问题,这一事实自然使我们对所推动的更改进行了更深入的审查。 在进入最终的根本原因部分之前,我们将对受影响的Log4j代码进行代码审查,例如,暴露于线程竞争条件下。

Apache Log4j 1.2.15代码审查

## org.apache.log4j.Category
/*** Call the appenders in the hierrachy starting at <code>this</code>. If no* appenders could be found, emit a warning.** <p>* This method calls all the appenders inherited from the hierarchy* circumventing any evaluation of whether to log or not to log the* particular log request.** @param event*            the event to log.*/public void callAppenders(LoggingEvent event) {int writes = 0;for (Category c = this; c != null; c = c.parent) {// Protected against simultaneous call to addAppender,// removeAppender,...synchronized (c) {if (c.aai != null) {writes += c.aai.appendLoopOnAppenders(event);}if (!c.additive) {break;}}}if (writes == 0) {repository.emitNoAppenderWarning(this);}

如您所见,Catelogry.callAppenders()在类别级别使用了一个同步块,这可能导致严重的并发负载下严重的线程竞争情况。 在这种情况下,使用重入式读-写锁会更合适(例如,这种锁策略允许并发的“读”但允许单个“写”)。 您可以在下面找到有关此已知Apache Log4j限制的参考以及一些可能的解决方案。

https://issues.apache.org/bugzilla/show_bug.cgi?id=41214

以上Log4j行为是否是我们问题的实际根本原因? 并没有那么快……请记住,只有在最近部署之后才暴露此问题。 真正的问题是,哪些应用程序更改从Apache Log4j日志记录API触发了此问题和副作用?

根本原因:完美的风暴!

对最近部署的更改的深入研究确实表明,已删除了子类加载器级别的某些Log4j库以及相关的“子优先”策略。 这个重构练习最终将Commons日志记录和Log4j的委派移到了类加载器级别。 问题是什么?

在进行此更改之前,日志记录事件在父类加载器的Weblogic Beehive Log4j调用与子类加载器的Web应用程序日志记录事件之间分配。 由于每个类加载器都有其自己的Log4j对象副本,因此线程竞争条件问题被分成两半,并且在当前负载条件下不暴露(屏蔽)。 重构之后,所有Log4j调用都移到了父类加载器(Java EE应用程序)中。 为Log4j组件(例如Category)添加了显着的并发级别。 并发级别的提高以及这种已知的Category.java线程争用/死锁行为对我们的生产环境是一场完美的风暴。

为了缓解此问题,还对环境应用了2种直接解决方案:

  • 在父类和子类加载器之间回滚重构和拆分的Log4j回调。
  • 将某些附加程序的日志记录级别从DEBUG降低到WARNING

当应用更改(例如与库和类加载器相关的更改)时,此问题案例再次增强了执行适当的测试和影响评估的重要性。 这样的更改在“表面”上看起来很简单,但可以触发一些深层的执行模式更改,从而使您的应用程序处于已知的线程竞争条件下。

还将探索对Apache Log4j 2(或其他日志记录API)的未来升级,因为它有望带来一些性能增强,从而解决一些线程竞争和可伸缩性方面的问题。

请提供任何评论或分享您与日志API相关的线程竞赛相关问题的经验。

祝您编程愉快,别忘了分享!

参考: Log4j线程死锁– Java EE支持模式和Java教程博客上的JCG合作伙伴 Pierre-Hugues Charbonneau的案例研究 。


翻译自: https://www.javacodegeeks.com/2012/09/log4j-thread-deadlock-case-study.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

堆栈跟踪从何而来?

我认为&#xff0c;阅读和理解堆栈跟踪是每个程序员都必须具备的一项基本技能&#xff0c;以便有效地解决每种JVM语言的问题&#xff08;另请参阅&#xff1a; 过滤日志中无关的堆栈跟踪行和首先记录引起异常的根本原因 &#xff09;。 那么我们可以从一个小测验开始吗&#xf…

@select 怎么写存储过程_MySQL4:存储过程和函数

什么是存储过程简单说&#xff0c;存储过程就是一条或多条SQL语句的集合&#xff0c;可视为批文件&#xff0c;但是起作用不仅限于批处理。本文主要讲解如何创建存储过程和存储函数以及变量的使用&#xff0c;如何调用、查看、修改、删除存储过程和存储函数等。使用的数据库和表…

跨域访问-预请求及跨域常见问题

预请求 参考&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#预请求 简而言之&#xff0c;在跨域并且尝试添加一些特殊头及自定义头的情况下&#xff0c;由于浏览器的安全机制&#xff0c;会加多一次OPTIONS预请求&#xff08;询问请求&am…

mysql查询优化之一:mysql查询优化常用方式

一、为什么查询速度会慢&#xff1f; 一个查询的生命周期大致可以按照顺序来看&#xff1a;从客户端&#xff0c;到服务器&#xff0c;然后在服务器上进行解析&#xff0c;生成执行计划&#xff0c;执行&#xff0c;并返回结果给客户端。其中在“执行”阶段包含了大量为了检索…

抖音右上角一个小黄点是什么_抖音官方入驻视频号,释放了一个什么样的信号?...

专注视频号观察第 328 篇这几天&#xff0c;视频号生态新入驻了一个企业号&#xff0c;在圈里引起不少的轰动&#xff0c;因为这个号的名字叫做———抖音。这件事在圈里引发不少的轰动&#xff0c;很多人惊叹&#xff1a;“连抖音都来开视频号了&#xff0c;你还在等什么&…

Jenkins:部署JEE工件

随着持续集成和持续交付的出现 &#xff0c;我们的构建被分为不同的步骤&#xff0c;以创建部署管道。 这些步骤中的一些步骤可以是例如编译和运行快速测试&#xff0c;运行慢速测试&#xff0c;运行自动验收测试或发布应用程序等。 部署流程的最后一步意味着将我们的产品&…

seafile 部署_Seafile开启webdav及读写性能测试

为什么要在seafile搞webdavSeafile 一直是一款可靠的文件同步web应用&#xff0c;经过个人测试&#xff0c;同一台机器上&#xff0c;seafile在传输文件时的速度比nextcloud要快&#xff08;可能也与php的设置有关系&#xff09;&#xff0c;这是seafile的优势。但是&#xff0…

Python--校园网爬虫记

查成绩&#xff0c;算分数&#xff0c;每年的综合测评都是个固定的过程&#xff0c;作为软件开发者&#xff0c;这些过程当然可以交给代码去做&#xff0c;通过脚本进行网络请求获取数据&#xff0c;然后直接进行计算得到基础分直接填表就好了&#xff0c;查成绩再手动计算既容…

Spring–添加SpringMVC –第1部分

欢迎来到本教程的第四部分。 在这一部分中&#xff0c;我们将使用Spring MVC编写控制器和视图&#xff0c;并考虑我们的REST模型。 我们必须做的第一件事&#xff0c;就是根据目前的情况制作一个Web应用程序。 我们将web / WEB-INF文件夹添加到我们的项目根目录。 在WEB-INF内创…

access month函数用法_学会了这7个EXCEL日期函数技巧,老板再让你加班,你找我!...

日期函数&#xff0c;常用年月日&#xff0c;时分秒&#xff0c;星期&#xff0c;季度&#xff0c;求差值等&#xff0c;学会以下几个函数&#xff0c;老板再让你加班&#xff0c;你找我&#xff01;1、记录当前时间(不随系统时间变化)NOW()函数与数据有效性结合&#xff0c;记…

meta 的作用 搜集

Meta标签中的format-detection属性及含义 format-detection翻译成中文的意思是“格式检测”&#xff0c;顾名思义&#xff0c;它是用来检测html里的一些格式的&#xff0c;那关于meta的format-detection属性主要是有以下几个设置&#xff1a;<meta name"format-detecti…

ThinkPHP 3.2.x 集成极光推送指北

3.2版本已经过了维护生命周期&#xff0c;官方已经不再维护&#xff0c;请及时更新至5.0版本 —— ThinkPHP 官方仓库 以上&#xff0c;如果有条件&#xff0c;请关闭这个页面&#xff0c;然后升级至 ThinkPHP 5&#xff0c;如果由于各种各样的原因无法升级至 TP 5 &#xff0c…

Java多线程——不变性与安全发布

1、不变性 某个对象在被创建后其状态就不能被修改&#xff0c;那么这个对象就称为不可变对象&#xff0c;不可变对象一定是线程安全的。不可变对象很简单。他们只有一种状态&#xff0c;并且该状态由构造函数来控制。 当满足以下条件时&#xff0c;对象才是不可变的&#xff1a…

中tr不能显示字符_垃圾文本识别中基本操作指南和错误总结,第三部分

创建模型需要用到机器学习的库&#xff0c;所以我们先下载sklearn库sklearn库下载完成后再输入库文件&#xff0c;就可以完美运行。然后就是划分测试集和训练集&#xff0c;需要注意的是&#xff0c;在从数据处理函数中导入数据时&#xff0c;足足运行了有将近30多秒&#xff0…

(转载)20分钟读懂程序集

转自&#xff1a;http://www.cnblogs.com/damonlan/p/3221347.html 说到程序集&#xff0c;我刚开始对这个名词特别的郁闷&#xff01;~。然后 前些天花了些时间 好好读了一下&#xff0c;现在比较清晰了&#xff0c;把一些书上看到的 记下来&#xff0c;以飨读者。希望没浪费你…

大数据胸_喂母乳会导致胸下垂?!你被这个谣言骗了多少年?

很多人认为&#xff0c;给宝宝喂奶会导致胸下垂。有些爱美的妈妈&#xff0c;甚至在宝宝出生6个月后就着急断奶。那么&#xff0c;喂奶真的会导致胸下垂么&#xff1f;给大家讲两个真实的调查结果哈~2004年的一次针对496名新妈妈的调查结果显示&#xff0c;有75%的母乳喂养母亲…

自制ACL+DHCP实验(初版)

&#xff08;实验用gns模拟器&#xff09; ACL 实验拓扑&#xff1a; 实验要求&#xff1a; 1.1.1.1→3.3.3.3 不通 11.11.11.11→3.3.3.3 通 2.2.2.2→3.3.3.3 通 实验步骤&#xff1a; 步骤一&#xff1a;基本配置 R1&#xff1a; R1#conf t R1(config)#int lo0 R1(config-if…

pil 图像最大值_第97天:图像库 PIL(二)

上节我们讲了 Python 的图像处理库 PIL 的基本图像处理功能&#xff0c;打开了 PIL 的神秘面纱。这节我们接着讲 PIL 的 Image 模块的常用方法。Image 模块的方法convertImage.convert(modeNone, matrixNone, ditherNone, palette0, colors256)参数说明&#xff1a;mode&#x…

c#的委托用法delegate

转载于:https://www.cnblogs.com/douzujun/p/6555886.html

np读取csv文件_被 Pandas read_csv 坑了

-- 不怕前路坎坷&#xff0c;只怕从一开始就走错了方向Pandas 是python的一个数据分析包&#xff0c;纳入了大量库和一些标准的数据模型&#xff0c;提供了高效地操作大型数据集所需的工具。Pandas 就是为解决数据分析任务生的&#xff0c;无论是数据分析还是机器学习项目数据预…