printStackTrace()造成的并发瓶颈
一 背景
在一次活动前的压测中,发现一个服务(平响为250ms左右)存在并发瓶颈,单实例的QPS压力从20升高到40后服务就雪崩了(平响急剧升高)。
通过<jstack -F>命令查看线程信息,发现很多线程BLOCKED在打印日志的地方:
Thread 39120: (state = BLOCKED)
- java.lang.Throwable.printStackTrace(java.lang.Throwable$PrintStreamOrWriter) @bci=25, line=653 (Compiled frame)
- java.lang.Throwable.printStackTrace(java.io.PrintStream) @bci=9, line=643 (Compiled frame)
- java.lang.Throwable.printStackTrace() @bci=4, line=634 (Compiled frame)
- org.apache.logging.log4j.core.Logger.logMessage(java.lang.String, org.apache.logging.log4j.Level, org.apache.logging.log4j.Marker, org.apache.logging.log4j.message.Message, java.lang.Throwable) @bci=103, line=144 (Interpreted frame)
- org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(java.lang.String, org.apache.logging.log4j.Level, org.apache.logging.log4j.Marker, org.apache.logging.log4j.message.Message, java.lang.Throwable) @bci=8, line=2091 (Compiled frame)
- org.apache.logging.log4j.spi.AbstractLogger.logMessage(java.lang.String, org.apache.logging.log4j.Level, org.apache.logging.log4j.Marker, java.lang.String, java.lang.Object[]) @bci=186, line=1999 (Interpreted frame)
- org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(java.lang.String, org.apache.logging.log4j.Level, org.apache.logging.log4j.Marker, java.lang.String, java.lang.Object[]) @bci=21, line=1868 (Interpreted frame)
- org.apache.logging.slf4j.Log4jLogger.info(java.lang.String, java.lang.Object) @bci=20, line=183 (Compiled frame)
该服务使用log4j-2.7打印日志,当时做了下面三个尝试:
- 从Logger改成asyncLogger,无效果;
- 减少日志量(只打印com.xxx.xxx包路径下的日志),单实例QPS压力升高到48后服务雪崩;
- 不打印info级别日志,单实例QPS压力到80服务依然正常;
很疑惑,为什么日志打印对服务性能的影响如此大?而且单实例的QPS压力只有20也太小了(并发数只有5 = 20 / 1000ms / 250ms)!
二 排查
分析<jstack -F>命令查出的线程信息,类Throwable的653行,printStackTrace()方法会对标准错误输出流(System.err)加同步锁(synchronized)。非常顺利,并发瓶颈的原因找到了!

但是,为什么logger.info会进入到Throwable.printStackTrace()呢?
错判1、jstack
怀疑<jstack -F>命令查出的线程信息有问题,尝试用<jstack -l>命令,提示错误信息"well-known file is not secure",搜了下是由于<pid>进程的所有者与执行jstack命令的用户不一致,使用sudo未成功(机器权限问题,阻塞未解决)。
错判2、GC
分析Throwable.printStackTrace()的上一行堆栈信息(类Logger的144行、类AbstractLogger的1992/1998行),怀疑是GC导致(历史经验,但讲不通),查看服务雪崩时的GC日志,发现确实GC频繁,搜了下CMS GC的相关文章,尝试修改JVM参数(内存大小、GC算法等),无效果。



错判3、log4j的bug
Remote debug到测试环境上,在Throwable.printStackTrace()处断点,发现必现异常(ArrayIndexOutOfBoundsException:4)。于是使用关键字log4j+ArrayIndexOutOfBoundsException搜了下,找到log4j2的官方issue(https://issues.apache.org/jira/browse/LOG4J2-1542),不太对,继续浏览该关键字其他的bug issue,没有找到答案,想着要不提一个bug?但升级log4j的版本到2.13后无效果。
柳暗花明
再次Remote debug到测试环境上,一步一步调试,发现会进入一些非本工程的代码且出现单词trace,想起来之前看到的通过字节码注入方式(jar包)打印trace日志的方案,怀疑是trace包内数组越界后catch异常同时e.printStackTrace()。最后找到trace包的提供者验证了该怀疑:

三 结论
通过字节码注入方式打印trace日志的jar包有一个数组越界的bug:
ThreadContext.put("XXX", ids[4]); // 数组ids大小为4
此处会抛出ArrayIndexOutOfBoundsException,该异常被catch后调用了e.printStackTrace(),而Throwable.printStackTrace()方法会对标准错误输出流(System.err)加同步锁(synchronized),从而造成了服务的并发瓶颈。
printStackTrace()造成的并发瓶颈的更多相关文章
- .NET线程池最大线程数的限制-记一次IIS并发瓶颈
.NET ThreadPool 最大线程数的限制 IIS并发瓶颈,有几个地方,IIS线程池的最大队列数,工作进程数,最大并发数.这些这里就不展开.主要是最近因为过度使用Task 导致的线程数占用过多, ...
- IIS并发瓶颈线程数的限制
.NET线程池最大线程数的限制-记一次IIS并发瓶颈 https://www.cnblogs.com/7rhythm/p/9964543.html .NET ThreadPool 最大线程数的限制 I ...
- Redis为什么可以支持那么大的并发访问量?为什么redis没有单点并发瓶颈?
一是redis使用内存 而是redis使用多路复用的IO模型: 现代的UNIX操作系统提供了select/poll/kqueue/epoll这样的系统调用,这些系统调用的功能是:你告知我一批套接字,当 ...
- JDK的多线程与并发库
1.创建多线程 public class MultiThread { public static void main(String[] args) { // 通过继承Thread类 Thread th ...
- Vertica并发DML操作性能瓶颈的产生与优化(转)
文章来源:中国联通网研院网优网管部IT技术研究团队 作者:陆昕 1. 引言 众所周知,MPP数据库以其分布式的超大存储能力以及列式的高速汇总能力,已经成为大数据分析比不可少的工具.Vertica就是这 ...
- 高并发的常见策略--大型web项目
一个运营的系统在正式上线后将会遇到各种层级的高并发请求,因此我们必须对此做出相应的策略和技术解决方案,首先我们需要认清系统的高并发由3个层面导致: 1. 传输层 大量用户对系统请求后,将会造成网络带宽 ...
- SSM实战——秒杀系统之高并发优化
一:高并发点 高并发出现在秒杀详情页,主要可能出现高并发问题的地方有:秒杀地址暴露.执行秒杀操作. 二:静态资源访问(页面)优化——CDN CDN,内容分发网络.我们把静态的资源(html/css/j ...
- Java并发测试
要求:模拟200个设备,尽量瞬间并发量达到200. 思路 第一种:线程池模拟200个线程——wait等待线程数达200——notifyAll唤醒所有线程 第二种:线程池模拟200个线程——阻塞线程—— ...
- Java并发编程--5.信号量和障碍器
Semaphore信号量 简介 它本质上是一个共享锁,限制访问公共资源的线程数目,它也被称为计数信号量acquire()许可一个线程, Semaphore – 1; 没有可用的许可时,Semaphor ...
随机推荐
- xss的变形--如何绕过xss过滤
我们可以通过构造xss代码进行各种变形,以绕过xss过滤器的检测 1.大小写检测 将payload进行大小写转化 如<Img SRC='#' Onerror="alert(/xss/ ...
- myBatis源码解析-二级缓存的实现方式
1. 前言 前面近一个月去写自己的mybatis框架了,对mybatis源码分析止步不前,此文继续前面的文章.开始分析mybatis一,二级缓存的实现.附上自己的项目github地址:https:// ...
- JAVA基础知识之面向对象编程知识汇总
JAVA基础课程部分面向对象已经学习完成,知识结构如下: 总体知识框架: 类的结构: 面向对象编程三大特征: 关键字和抽象类接口等: 常见知识汇总: 成员变量和局部变量比较 有无返回值方法比较: 权限 ...
- 从CSDN博客下载的图片如何无损去水印
如果你想下载别人CSDN博客文章中很好看的图片,但却有水印 想要下载去水印的图片,可以先鼠标右击该图片,选择复制图片地址 https://img-blog.csdnimg.cn/20200916140 ...
- 分布式系统监视zabbix讲解四之可视化
图形 概述 随着大量的监控数据被采集到Zabbix中,如果用户可以以可视化的表现形式来查看发生了什么事情,那么和仅仅只有数字的表现形式比起来则更加轻松. 以下是进行图形设置的地方.图形可以一目了然地掌 ...
- 关于弹性布局flex
什么时候使用flex布局? 当页面排版涉及左右浮动.垂直居中等时,应使用flex布局来避免传统的盒式布局带来的一些Bug. 如何使用flex布局? 在目标元素的父元素设置csss属性.display: ...
- python获取某视频网站视频
还是老生常谈的操作 import requests import os from bs4 import BeautifulSoup from urllib.parse import urljoin h ...
- Windows无法安装到GPT格式磁盘的根本解决办法 - 初学者系列 - 学习者系列文章
上次在MSDN系统QQ群里有朋友问到在安装操作系统的时候有个问题:Windows无法安装到GPT格式磁盘,见图: 我在这里说下,使用网上方法的都是小白,就是说网上那些都是小白.下面介绍如何正确安装操作 ...
- nginx 1.12安装
准备工作 使用root用户安装. 到nginx官网下载Linux源码或者执行:wget http://nginx.org/download/nginx-1.12.2.tar.gz. 到pcre站点下载 ...
- Centos-分割文件-split
split 分割文件,将一个文件分割为多个 相关选项 -b 指定文件大小,可以在size后面添加单位后缀,b表示512字节,k表示1KB,m表示MB -n 指定分割文件的长度,默认为1000行 -d ...