提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
原创|使用教程|编辑:郑恭琳|2020-07-13 14:39:57.827|阅读 117 次
概述:在本文中,学习如何监控Java线程以了解应用程序中引起性能问题的特定代码行。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
相关链接:
在本文中,学习如何监控Java线程以了解应用程序中引起性能问题的特定代码行。
与线程相关的问题可能会以某种方式对Web API应用程序的性能产生不利影响,这些方式通常难以诊断且难以解决。清晰了解线程的行为对于实现最佳性能至关重要。在本文中,我将向您展示如何使用Parasoft SOAtest的负载测试JVM线程监视器来查看JVM的线程活动,以及动态统计图和可配置的线程转储图,这些图可以指向造成性能损失的代码行。线程使用效率低下。使用Parasoft SOAtest的负载测试模块,您可以将任何功能测试转换为负载和性能测试。
我们将跟随一个假想的Java开发团队,在创建Web API应用程序时遇到一些常见的线程问题,并诊断一些与线程相关的常见性能问题。之后,我们将看一下实际应用程序的更复杂的示例。(请注意,以下示例中的一些次优代码是出于演示目的而有意添加的。)
我们假设的Java开发团队着手进行一个新项目——REST API银行应用程序。该团队建立了一个持续集成(CI)基础结构来支持新项目,其中包括使用Parasoft SOAtest的负载测试模块进行的定期CI工作,以连续测试新应用程序的性能。(有关如何设置自动化性能测试的更多详细信息,请参阅我以前的文章《DevOps交付管道中的负载和性能测试》。)
银行应用程序版本1:初始实施中的竞争条件
Bank应用程序代码开始增长,并且测试正在运行。但是,团队注意到,在实施新的转帐操作之后,银行应用程序开始在较高的负载下出现零星的故障。该失败来自帐户验证方法,该方法有时会在透支保护帐户中发现负余额。帐户验证失败会导致异常和API的HTTP 500响应。开发人员怀疑这可能是由处理同一帐户上的并发转移操作的不同线程调用的IAccount.withdraw方法中的竞争条件引起的:
13: public boolean transfer(IAccount from, IAccount to, int amount) { 14: if (from.withdraw(amount)) { 15: to.deposit(amount); 16: return true; 17: } 18: return false; 19: }
银行应用程序版本2:添加同步
开发人员决定在转帐操作中同步对帐户的访问,以防止出现可疑的比赛情况:
14: public boolean transfer(IAccount from, IAccount to, int amount) { 15: synchronized (to) { 16: synchronized (from) { 17: if (from.withdraw(amount)) { 18: to.deposit(amount); 19: return true; 20: } 21: } 22: } 23: return false; 24: }
该团队还将JVM线程监视器添加到针对REST API应用程序运行的负载测试项目。该监视器将提供死锁、阻塞、驻留和总线程的图表,并将记录这些状态下的线程转储。
代码更改被推送到存储库,并由CI性能测试过程获取。第二天,开发人员发现性能测试在一夜之间失败了。开始进行转帐操作性能测试后不久,Bank应用程序停止响应。快速查看“负载测试”报告中的“JVM线程监视器”图,可以发现Bank应用程序中存在死锁线程(请参见图1.a)。死锁详细信息由JVM线程监视器保存为报告的一部分,并显示了导致死锁的确切代码行(请参见清单1.b)。
图1.a-被测应用程序(AUT)中死锁的线程数。
DEADLOCKED thread: http-nio-8080-exec-20 com.parasoft.demo.bank.v2.ATM_2.transfer:15 com.parasoft.demo.bank.ATM.transfer:21 ... Blocked by: DEADLOCKED thread: http-nio-8080-exec-7 com.parasoft.demo.bank.v2.ATM_2.transfer:16 com.parasoft.demo.bank.ATM.transfer:21 com.parasoft.demo.bank.v2.RestController_2.transfer:29 sun.reflect.GeneratedMethodAccessor58.invoke:-1 sun.reflect.DelegatingMethodAccessorImpl.invoke:-1 java.lang.reflect.Method.invoke:-1 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke:209
清单1.b——JVM线程监视器保存的死锁详细信息
银行应用程序版本3:解决僵局
银行应用程序开发人员决定通过在单个全局对象上进行同步来解决死锁,并修改传输方法代码,如下所示:
14: public boolean transfer(IAccount from, IAccount to, int amount) { 15: synchronized (Account.class) { 17: if (from.withdraw(amount)) { 18: to.deposit(amount); 19: return true; 20: } 21: } 22: return false; 23: }
该更改解决了版本2的死锁问题和版本1的竞争状况,但是平均传输操作响应时间从30毫秒增加到150毫秒以上,增加了五倍以上(见图2.a)。JVM线程监视器的BlockedRatio图形显示,在负载测试执行期间,有60%到75%的应用程序线程处于BLOCKED状态(请参见图2.b)。监视器保存的详细信息表明,尝试进入第15行的全局同步部分时,应用程序线程被阻止(请参见清单2.c)。
解决银行申请僵局
BLOCKED thread: http-nio-8080-exec-4 com.parasoft.demo.bank.v3.ATM_3.transfer:15 com.parasoft.demo.bank.ATM.transfer:21 com.parasoft.demo.bank.v3.RestController_3.transfer:29 ... Blocked by: SLEEPING thread: http-nio-8080-exec-8 java.lang.Thread.sleep:-2 com.parasoft.demo.bank.Account.doWithdraw:64 com.parasoft.demo.bank.Account.withdraw:31
清单2.c——JVM线程监视器保存的阻塞线程详细信息
银行应用程序版本4:提高同步性能
开发团队寻找一种解决方案,该解决方案可以解决竞态条件而又不会引入死锁和损害应用程序的响应能力,并且经过一些研究找到了使用java.util.concurrent.locks.ReentrantLock类的有前途的解决方案:
19: private boolean doTransfer(Account from, Account to, int amount) { 20: try { 21: acquireLocks(from.getReentrantLock(), to.getReentrantLock()); 22: if (from.withdraw(amount)) { 23: to.deposit(amount); 24: return true; 25: } 26: return false; 27: } finally { 28: releaseLocks(from.getReentrantLock(), to.getReentrantLock()); 29: } 30:
图3a中的图形在红色图形中显示了版本4(优化锁定)的银行应用程序转移操作的响应时间,在蓝色图形中显示了版本3(全局对象同步)的响应时间,在绿色图形中显示了版本1(非同步转移操作)的响应时间。这些图表明,由于锁定优化,转移操作性能得到了显着改善。同步(红色图)和非同步(绿色图)传输操作之间的细微差别是防止竞争条件的可接受价格。
图3.a——银行应用程序版本4(红色)、版本3(蓝色)和版本1(绿色)的传输操作响应时间。
示例1:增加应用程序响应时间
上面的“银行应用程序”示例旨在说明如何解决由线程问题导致的性能下降的典型隔离情况。实际情况可能更复杂——图4中的图形显示了一个生产REST API应用程序的示例,该应用程序的响应时间随着性能测试的进行而不断增长。在测试的上半部分,应用程序响应时间以较低的速率增长,在下半部分中以较高的速率增长(见图4.a)。在测试的前半部分,响应时间的增长与应用程序线程在“阻塞”状态下花费的总时间相关(请参见图4.b)。在测试的后半部分,响应时间的增长与处于PARKED状态的应用程序线程数相关。负载测试JVM线程监视器捕获的堆栈跟踪提供了详细信息:一个指向同步块,该块导致在BLOCKED状态下花费过多时间。另一个指出使用java.util.concurrent.locks类进行同步的代码行,该类负责使线程保持在PARKED状态。在优化了这些代码区域之后,两个性能问题都得到解决。
示例2:应用程序响应时间中的偶发事件
负载测试JVM线程监视器可以非常有用地捕获与线程相关的罕见问题的详细信息,尤其是在性能测试是自动执行且定期运行的情况下。图5中的图形显示了生产REST API应用程序,该应用程序在平均和最大响应时间上都有间歇性的峰值(见图5.a)。
应用程序响应时间的这种峰值通常可能是由于JVM垃圾收集器配置欠佳所致,但是在这种情况下,BlockedTime监视器中的相关峰值(请参见图5.b)指出线程同步是问题的根源。BlockedThreads监视器通过捕获阻塞线程和阻塞线程的堆栈跟踪,在这里提供了更多帮助。重要的是要了解BlockedTime和BlockedThreads监视器之间的区别。
BlockedTime监视程序显示JVM线程在监视程序调用之间处于BLOCKED状态所花费的累积时间,而BlockedThreads监视程序则对JVM线程进行定期快照,并在这些快照中搜索被阻止的线程。因此,BlockedTime监视器在检测线程阻塞方面更为可靠,但它只是警告您存在线程阻塞问题。BlockedThreads监视器因为它获取常规线程快照而可能会丢失某些线程阻塞事件,但从正面来看,当它捕获此类事件时,它会提供导致阻塞的详细信息。因此,BlockedThreads监视器是否将捕获与代码相关的阻塞状态的详细信息是一个统计问题,但是如果定期进行性能测试,您很快就会在BlockedThreads图中看到峰值(参见图5.c),这意味着已捕获阻塞和阻塞线程的详细信息。这些详细信息将使您指向导致应用程序响应时间极少出现尖峰的代码行。
创建性能回归控件
负载测试JVM线程监视器除了是一种有效的诊断工具外,还可以用于创建与线程相关的问题的性能回归控件。发现并解决了此类性能问题后,请为其创建性能回归测试。该测试将包括现有或新的性能测试运行以及新的回归控件。对于Parasoft负载测试,这将是相关JVM线程监控器通道的QoS监控器指标。例如,对于示例1(图4)中描述的问题,创建一个负载测试QoS监视器度量标准,该度量标准检查应用程序线程处于“阻塞”状态所花费的时间,以及另一个度量标准,用于检查处于“已驻留”状态的线程数。在Java应用程序中创建命名线程始终是一个好主意,这将使您可以将性能回归控件应用于经过名称过滤的一组线程。
下表总结了哪些线程监视器通道以及何时使用:
线程监控器通道 |
何时使用 |
死锁线程
MonitorDeadlockedThreads
|
一般来说,死锁可以说是与线程相关的最严重的问题,它可能会完全破坏应用程序的功能。 |
阻塞线程 封锁时间
封锁比率
BlockedCount |
常规情况中,在“阻塞”状态下花费的时间过多或“阻塞”线程数通常会导致性能下降。监视这些参数中的至少一个。也可用于性能回归控制。 |
停放线程 |
一般来说,处于PARKED状态的线程数量过多可能表明java.util.concurrent.locks类的使用不正确以及其他线程问题。也可用于性能回归控制。 |
总线程 |
常规情况中,用于将处于阻塞,驻留或其他状态的线程数与线程总数进行比较。 也可用于性能回归控制。 |
睡眠线程 等待线程 等待时间 等待比率 等待计数 |
偶尔,用于与这些状态相关的性能回归控制和探索性测试。 |
新线程 未知线程 |
很少,用于与这些线程状态相关的性能回归控件。 |
Parasoft的JVM Threads Monitor是一种有效的诊断工具,可检测与线程相关的JVM性能问题并创建高级性能回归控件。与SOAtest的负载测试连续体结合使用时,JVM线程监视器通过记录相关的线程详细信息(这些代码行导致性能不佳)来帮助消除重现性能问题的步骤,并帮助您提高应用程序性能以及开发人员和质量保证生产率。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@cahobeh.cn
本文探讨 SQL Server 中 NULL 和空值之间的区别,并讨论如何有效地处理它们。
Unity 是一款功能极其丰富的游戏引擎,允许开发人员将各种媒体集成到他们的项目中。但是,它缺少最令人兴奋的功能之一 - 将 Web 内容(例如 HTML、CSS 和 JavaScript)直接渲染到 3D 场景中的纹理上的能力。在本文中,我们将介绍如何使用 DotNetBrowser 在 Unity3D 中将 Web 内容渲染为纹理。
DevExpress v24.2帮助文档正式发布上线了,请按版本按需下载~
本教程将向您展示如何用MyEclipse构建一个Web项目,欢迎下载最新版IDE体验!
人工智能和机器学习赋能 API 和 Web 服务测试
Parasoft SOAtest with Load Test通过使用现有的功能测试来解锁早期的负载和性能测试
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@cahobeh.cn
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢