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 ...
随机推荐
- unittest单元测试执行用例的顺序
打印结果如下:
- 【小白学PyTorch】12 SENet详解及PyTorch实现
文章来自微信公众号[机器学习炼丹术].我是炼丹兄,有什么问题都可以来找我交流,近期建立了微信交流群,也在朋友圈抽奖赠书十多本了.我的微信是cyx645016617,欢迎各位朋友. 参考目录: @ 目录 ...
- flutter权限管理permission_handler
flutter权限管理permission_handler 添加依赖 #权限 permission_handler: ^3.0.0 使用 在android的mainfest中添加权限: <use ...
- Envoy 代理中的请求的生命周期
Envoy 代理中的请求的生命周期 翻译自Envoy官方文档. 目录 Envoy 代理中的请求的生命周期 术语 网络拓扑 配置 高层架构 请求流 总览 1.Listener TCP连接的接收 2.监听 ...
- 虚拟机Ubuntu(18.04.2)下安装配置Hadoop(2.9.2)(伪分布式+Java8)
[本文结构] [1]安装Hadoop前的准备工作 [1.1] 创建新用户 [1.2] 更新APT [1.3] 安装SSH [1.4] 安装Java环境 [2]安装和配置hadoop [2.1] Had ...
- 并发编程(八)Lock锁
一.引言 线程并发的过程中,肯定会设计到一个变量共享的概念,那么我们在多线程运行过程中,怎么保证每个先拿获取的变量信息都是最新且有序的呢?这一篇我们来专门学习一下Lock锁. 我们先来了解几个概念: ...
- 《Web安全攻防渗透测试实战指南》 各类型 SQL注入 实验过程整理
Union注入 https://www.jianshu.com/p/8a11bf55aaee Boolean注入 https://www.jianshu.com/p/e4086f59812d 报错注入 ...
- Spring学习(四)--Spring的IOC
1.BeaDefinition的Resource定位 (1)直接使用BeanDefinitionFactory 定义一个Resource来定位容器使用的BeanDefinition. Resource ...
- Centos-检查文件系统并尝试修复-fsck
fsck 检查文件系统并尝试修改错误,修复对象为设备,本质上是调用 /sbin/fsck.filesystemName 命令, filesystemName是指定设备的文件系统类型,如图分区中有文件丢 ...
- 中心极限定理(为什么y服从高斯分布)
因为每一条数据都服从IID原则: 根据中心极限定理,当数据增加的时候,样本均值的分布慢慢变成正态分布 不管分布式什么分布,累加起来都是高斯分布 As sum increases, sum of non ...