log4j 多线程死锁问题_Log4j线程死锁–案例研究

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

log4j 多线程死锁问题

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

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

相关文章

谜题6:多重转型

转型被用来将一个数值从一种类型转换到另一种类型。下面的程序连续使用了三个转型。那么它到底会打印出什么呢&#xff1f; public class Multicast{public static void main (String[] args){ System.out.println((int)(char)(byte) -1); } } 无论你怎样分析这个程序&#xf…

nginx 简介篇

Nginx(发音为“engine x”)是一款由俄罗斯软件工程师Igor Sysoev写的开源的web服务器。自从2004年发布以来nginx一直关注于高性能、高并发、低内存的使用,另外还有一些特色的Web服务器功能&#xff0c;如负载均衡、缓存、访问和带宽控制以及能够有效的与各种应用集成这些特点使…

MySQl Got a packet bigger than ' max_allowed_packet' bytes

在my.ini中最后增加一行max_allowed_packet16M即可&#xff08;根据实际情况设置&#xff09;。

防止缓存爆炸的快速提示

在很多情况下&#xff0c;您都可以从应用程序中的常用对象缓存中受益&#xff0c;特别是在面向Web和微服务的环境中。 您可以在Java中执行的最简单的缓存类型可能是引入一个私有HashMap&#xff0c;在计算对象之前先对其进行查询&#xff0c;以确保您不会重复执行此操作。 这是…

设置过mysql远程连接后仍然无法进行远程连接 (mysql mysql报错2003 can't connect)

1、在控制面板上-〉系统和安全-》 window是防火墙-》打开或关闭Windows防火墙-》关闭Windows防火墙&#xff08;不推荐),这样mysql就可以进行远程连接但是有一个问题就是不安全。所以采取以下的方法。 2、 解决方法: (1.控制面板 打开防火墙,在进入规则中新建一个规则. (2. 选…

go 二进制程序守护_图解 Go 程序是怎样跑起来的

Go语言中文网&#xff0c;致力于每日分享编码、开源等知识&#xff0c;欢迎关注我&#xff0c;会有意想不到的收获&#xff01;刚开始写这篇文章的时候&#xff0c;目标非常大&#xff0c;想要探索 Go 程序的一生&#xff1a;编码、编译、汇编、链接、运行、退出。它的每一步具…

nginx 配置从零开始

作为一个 nginx 的初学者记录一下从零起步的点滴。 基本概念 Nginx 最常的用途是提供反向代理服务&#xff0c;那么什么反向代理呢&#xff1f;正向代理相信很多大陆同胞都在这片神奇的土地上用过了&#xff0c;原理大致如下图&#xff1a; 代理服务器作为客户端这边的中介接…

[MySQL]经常无故崩溃amd.dll , boot1.exe病毒

近段时间有大量客户反馈&#xff0c;mysql经常无故崩溃或者是在C盘根目录出现 amd.dll , boot1.exe文件此病毒为mysql数据库的漏洞所导致&#xff0c;目前所有版本均有此问题目前已经有大量的客户出现这个问题请收到此消息后立刻检查自己的C盘目录&#xff0c;是否有amd.dll , …

Spring启动和缓存抽象

缓存是大多数应用程序的主要组成部分&#xff0c;只要我们设法避免磁盘访问&#xff0c;缓存就会保持强劲。 Spring对各种配置的缓存提供了强大的支持 。 您可以根据需要简单地开始&#xff0c;然后进行更多可定制的操作。 这将是spring提供的最简单的缓存形式的示例。 Sprin…

关于python的if条件语句,whilefor循环等的简单说。

if 条件1:# 语句体# elif 条件2:# 语句体# elif 条件3:# 语句体# else:# 语句体 #从键盘输入一个值# num 8# num1 int(input("请输入一个整数&#xff1a;"))if num1 num: print("您猜对了")elif num1 < num:print("您猜小了")else:print(…

haproxy配置代理tomcat和nginx_你真的掌握LVS、Nginx及HAProxy工作原理吗?

当前大多数的互联网系统都使用了服务器集群技术&#xff0c;集群是将相同服务部署在多台服务器上构成一个集群整体对外提供服务。图片来自 Unsplash这些集群可以是 Web 应用服务器集群&#xff0c;也可以是数据库服务器集群&#xff0c;还可以是分布式缓存服务器集群等等。在实…

PostgreSQL新手入门

自从MySQL被Oracle收购以后&#xff0c;PostgreSQL逐渐成为开源关系型数据库的首选。 本文介绍PostgreSQL的安装和基本用法&#xff0c;供初次使用者上手。以下内容基于Debian操作系统&#xff0c;其他操作系统实在没有精力兼顾&#xff0c;但是大部分内容应该普遍适用。 一、安…

使用Junit和Easytest进行数据驱动的测试

在本文中&#xff0c;我们将看到如何使用Junit进行数据驱动的测试。 为此&#xff0c;我将使用一个名为EasyTest的库。 众所周知&#xff0c;对于TestNG&#xff0c;它已内置了数据提供程序。 通过简单的测试&#xff0c;我们可以使用Junit进行数据驱动的测试。 什么是数据驱…

Matlab代码的耗时分析、优化、加速

Profile(分析&#xff09; 在加速你的matlab程序之前&#xff0c;你需要知道你的代码哪一部分运行最慢。matlab提供个简单的机制&#xff0c;让你能够知道你 的代码的某一部分运行所占用CPU时间。通过在代码段开始添加tic&#xff0c;及在结束添加toc&#xff1b;matlab就能计算…

python整数逆序输出_利用Python实现倒序任意整数

这是很早以前学习C时候做过的一个练习题&#xff0c;题目的要求大概是把用户输入的三位数倒序输出&#xff0c;比如说用户输入123&#xff0c;然后程序应该输出的结果是321。如果遇到用户输入100&#xff0c;那么程序应该输出1。然后我给扩展一下&#xff0c;这个程序来适应于三…

谜题61:日期游戏

下面的程序演练了Date和Calendar类的某些基本特性&#xff0c;它会打印出什么呢&#xff1f; import java.util.*;public class DatingGame {public static void main(String[ ] args) { Calendar cal Calendar.getInstance(); cal.set(1999, 12, 31); // Year, Month, Day Sy…

Java 中Thread的sleep、join、yield方法解析

1.Thread中sleep方法作用是使当前线程等待&#xff0c;其他线程开始执行&#xff0c;如果有线程锁&#xff0c;sleep不会让出锁 没有加锁代码如下&#xff1a; public class SynchronizedSleepMethod {public static void main(String[] args) {MoneyMethod moneyMethod new M…

MySQL与PostgreSQL:该选择哪个开源数据库?哪一个更好?

Naresh Kumar是一位软件工程师与热情的博主&#xff0c;对编程与新事物充满了激情和兴趣。近日&#xff0c;Naresh撰写了一篇博文&#xff0c;对开源世界最常见的两种数据库MySQL与PostgreSQL的特点进行了详尽的分析和比对。 如果打算为项目选择一款免费、开源的数据库&#x…

jquery select change事件_jQuery实现省市联动效果

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><select name"province"id"province"><option >请选择</option></select><selec…

MATLAB上的GPU加速计算——学习笔记

MATLAB目前只支持Nvidia的显卡。如果你的显卡是AMD的或者是Intel的&#xff0c;就得考虑另寻它路了。 MATLAB可谓工程计算中的神器&#xff0c;一方面它自带丰富的函数库&#xff0c;另一方面它所有的数据都是内建的矩阵类型&#xff0c;最后画图也方便&#xff0c;因此解决一…