推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

linux c语言 定时器 云上Java System Profiling与Debugging

   2023-07-07 网络整理佚名1270
核心提示:比如网络隔离,导致无法直接使用有简便图形界面的去远程调试线上服务器。而对于云上难以复现、偶发性的问题,我们推荐使用在线调试平台对运行中的程序进行在线调试。因为在用户态程序中,无法像perf一样采用硬件中断,而是采用了定时信号(linux系统提供了一种定时器)。在定时器信号的处理函数中去回溯Java调用栈。回溯Java调用栈,使用了的一个非标准接口(据说商业软件的也是用的这个接口)。

从1995年Java 1.0 beta发布到现在,已经过去了20年。 Java的发明起源于嵌入式领域,但后来Java的发展却意外地在企业级应用领域占据了几乎统治地位。 阿里巴巴和支付宝(后来的蚂蚁金服)的大部分业务代码都是用Java编写的。 在生日之年,我们用这篇文章记录一下蚂蚁金服内部金融云环境中Java系统的经验和实践,并与大家分享,有着非常特殊的意义。 希望读者能够学到一些解决问题的思路。

在线/困难

中心化架构时代,应用按照大的业务功能划分为不同的模块或系统,在系统类型、数量、依赖等方面都相对可控。 一旦线上出现问题,很容易离线模拟分析。 然而,基于分布式架构系统的应用具有数量多、规模大、环境复杂的特点。 这些特性带来了很多问题,这些问题不是单一工具和系统所能解决的。 这里我们只关注给我们和我们的工作带来的挑战。

由于安全问题增加了访问控制。 SOA架构下,系统通过服务进行通信,但并不是所有的服务都能公开,也不是所有的客户端都是可信的。 因此,系统中增加了各种控制和权限设置。 这些安全措施给公司和工作带来了很大的麻烦。 例如,网络隔离使得无法直接使用简单的图形界面远程调试在线服务器。 即使登录服务器并使用命令行工具,仍然面临登录账号权限、sudo权限、源码查看权限等一系列权限申请问题,大大降低了问题定位的效率。

类库依赖关系复杂,版本分散。 类库的复用是系统设计的经典规则,但随着系统规模增大到数百甚至上千规模,各个系统对类库的依赖会形成多种排列组合,版本也随之变化。类库的分布也会出现碎片化。 有时需要针对具体系统找到相关类库的版本号,然后找到对应的代码,比较麻烦。

架构规范制定容易,维护困难:当单体数量较多且相对不可控时,架构规范就显得尤为重要。 但由于系统分散且规模庞大,维护架构规范将会非常困难。 每次引入系统变更时,都会引入新的复杂性和破坏架构规范的风险,代码也会生成一些“”。 因此,在分布式环境下,问题的长尾效应是非常明显的。 在一个系统中发现的错误或性能问题可能会在很长一段时间内再次出现在不同的系统中。 相反,一些一般认为发生概率较低的问题,当机器数量较多时,仍然很有可能发生。

请求调用链接跟踪困难。 模块和系统拆解得越详细,服务就越多,系统之间的依赖关系就越复杂。 在SOA架构下,随着系统规模的增大,服务的上下游依赖关系将会错综复杂,系统与平台之间的依赖关系的复杂性也会急剧增加。 一个分布式请求从用户点击到收到响应往往要经过N个系统,或者N个运行不同系统的服务器,每个服务器同时并发处理很多请求。 这就要求系统能够简洁、清晰地展示分析结果,以便开发人员能够快速判断其中一个节点是否存在问题,以及问题的严重程度,并更加关注各个集群中调用链路的走向等上层信息分析。 毕竟,在大规模的分布式系统中,保证系统的可用性是第一要务,不应该长期纠结于一个细节。

线上问题难以重现:线上问题有时与环境有关,线下难以重现,有时是零星发生,难以捕捉。 随着系统复杂度的增加,在线问题的快速定位和实时分析将是一个巨大的挑战,而这只能靠人来解决。 经验是积累起来的,经验是很难传承的。 一旦关键人员流失,整个团队解决问题的能力就会下降,这个代价是难以接受的。 需要有一个方便的地方来积累经验,而更好的做法是将这些宝贵的经验沉淀到工具和工具中。

与and相关的,有蚂蚁金服内部开发并使用的两个产品:and。 顾名思义,主要用于,主要用于。 蚂蚁金服也有自己的定制化为主,大部分功能都可以直接运行在标准JDK上,但是一些高级功能需要配合我们定制的JDK使用,这个我们后面会讲到。 针对上述挑战,我们的产品具有以下特点。

常见问题的解决方案

下面介绍一下在处理一些常见问题时使用系统与使用传统工具在流程和思路上的一些差异。 如果新上线的系统处理能力没有达到我们的预期,或者老系统的处理速度突然下降,或者频繁抛出异常,这些都会提示我们系统存在性能问题,需要进行优化。 遇到的性能问题包括OOM、CPU占用率高、负载高、GC频繁等。OOM的现象是Java进程直接退出,在错误日志中可以看到异常。 如果您发现自己经常这样做,那么通常是 OOM 的先兆。 解决此类问题的主要手段是分析。

JDK有自带的jmap工具,使用命令 jmap-dump:live,=b,file=heap.bin 按照格式转储Java进程的堆内存

图片

到名为 heap.bin 的文件。 命令中的live参数表示在转储之前执行一次,以确保转储的对象尽可能活,减少heap.bin的大小,减少工具的分析时间。 是否设置该参数需要根据具体问题而定。 例如,如果你想知道为什么旧区增长得这么快,添加了哪些对象? 这时候可能就不需要添加live参数了,只要连续做两次,然后做一下内存对比,就知道这段时间添加了哪些对象了。 但如果你想找到一些长时间没有释放的对象,并分析根引用树,那么在dump中添加live参数是比较合理的。

常用的分析工具是rTool,简称MAT。 这个工具很强大,但是因为太强大,所以初学者的学习成本比较高。 其次,它是客户端程序,需要安装在本地,因此也受到本地机器性能的限制。 例如本地内存为4GB,堆文件大小为8GB。 此时MAT无法完成分析工作。 因为MAT本身也是一个Java程序,所以在分析大堆的时候会不断的做GC,甚至等待异常情况,导致分析没有结果。 针对这些问题,我们的性能分析产品减少了一些不常用的功能,以保证大多数用户的易用性。 另外,它是一个Web系统,运行在服务器上,分析能力不再受开发者机器性能的限制,WebUI操作的方式也大大减轻了开发者的工作量。

CPU高的问题需要分层考虑。 首先检查操作系统层面的一些原因,比如频繁,可能会导致Java应用程序在内核态花费更多的时间。 您还可以检查 JVM 内部的 CPU 使用情况,例如 JIT 和 GC 线程。 如果JIT线程的CPU使用率较高,则需要检查或其他JIT相关参数设置是否正确; 如果是gc线程,则需要进一步分析gclog,然后调整GC相关参数。

排除以上原因后,基本可以确认是Java代码的问题。 可以使用提供的函数查看热点方法,后面会详细介绍。

高负载意味着有很多正在运行的线程或者运行队列中有很多线程。 这时候就可以使用 dump来检查。 对于线程转储,可以使用JDK自带的工具,只需执行命令,该进程的所有Java线程都会被转储。 如果仍然想跟踪堆栈,则需要增加-m参数。 得到线程转储后,根据线程状态进行分类【注:JVM中转储的线程日志中线程的状态并不是100%准确。 详情请参考:多个线程持有锁? 】。 对于同一个系统,处于高Load机器状态的线程数量必须多于处于低Load机器状态的线程数量。 我们可以分析这些额外的线程在做什么,从而找到Load高的原因。

对于线程分析,不仅提供从状态粒度的分析,还从锁粒度和热点栈粒度进行统计分析,可以帮助用户看到哪些线程受到了锁的影响,哪些线程持有这个锁,哪些线程正在等待对于这个锁,对于由于同步锁的问题导致的线程数突然增加,通过分析结果基本上可以看出问题出在哪里。 同样,还提供了哪些线程或正在运行的线程正在执行堆栈上的方法的统计信息,方便用户排查Load等问题。

图2

频繁GC是Java程序中最常见的问题之一,大多数情况下是由于相关参数配置不合理造成的。 相关的设置参数有很多。 要找到更合理的参数设置,首先必须对应用程序的内存情况有一个整体的了解:比如应用程序稳定运行后,内存是多少? 这会影响 -Xmx/-Xms/-Xmn 设置的大小【注:/合着提供很好的指南】; 对象temp对象与运行时创建的对象的大致比例,会影响堆的旧区和新区的设置比例。

通常有两种方法来检查GC情况。 一是打开gclog相关参数,事后分析gclog; 二是实时获取GC信息。 您可以打开JMX界面获取相关信息,或者使用jstat命令。

gclog分析是一件苦差事,因为gclog输出的信息很多,但很多时候我们需要关注长期趋势和整体统计信息,而手动分析太耗时。 还有一些很好的 gclog 分析工具,例如。 除了做类似的工作之外,如前所述,系统本身部署在金融云中,这意味着用户可以通过简单的WebUI操作轻松地将gclog直接复制到其中进行分析。 此外,我们还在开发一些更高级的功能。 gclog分析完成后,我们会给用户一些直接的建议,比如时间是否太长,如何调整设置的GC参数,GC占用的CPU时间是否太长等。 等待。

在实时获取GC信息方面,该插件做得很好,非常直观地展示了当前应用的GC状态,但分析还不够。 使用相同的方法来获取实时GC信息,以显示更有价值且具有统计意义的信息。 对于云端难以重现、偶发的问题,建议使用在线调试平台对运行程序进行在线调试。 在线调试的原则是方便用户简单快捷地打开调试器,而不影响应用程序的正常运行。 基于这个思想,将其设计为一个基于Jetty的Web服务器,用户可以使用浏览器发送调试命令。 调试器使用JDWP协议通过网络连接到被调试的JVM后端,无需重启JVM即可动态开启JVM调试功能。 它的回归。

JDK定制

最后说说我们在. 主要添加了 Debug-on-Late-(aka.DOLA)、Fast--(aka.FSAB) 等功能【注:我们还将与社区讨论如何标准化这些更改并为社区做出贡献,其中一些部分已经讨论过了]。

DOLA的意思是可以在不重新启动JVM的情况下开启“有限”的Java调试功能。 这个功能对于生产环境的调试非常有意义。 虽然目前主流的商业JVM基本都宣称“full-”,但在实际使用中,我们在生产环境中直接启用JVM的调试能力的人并不多。 可以打开JVM调试],因为实际上它对应用程序的性能还是有影响的。 例如,对于JIT的影响,在调试模式下该功能会自动关闭,一些解释器也可能会受到影响而消失。 因此,当线上出现问题时,往往需要重新启动有问题的JVM并打开Debug功能进行调试。 尴尬的是,重新启动 JVM 往往会导致问题场景难以重现。 这就是我们改造JDK、加入DOLA的初衷。

DOLA允许用户通过jcmd命令动态启用JVM的“有限”调试功能。 通过jcmd命令,用户可以在被调试的JVM上设置断点并查看变量的值。 这两个功能足以帮助我们在线发现和诊断大部分问题。

DOLA定制解决的难点主要在于以下几个方面。

FSAB的作用是配合系统的调试模式。 这种模式是借用过来的,它的应用场景是:在云环境中,传统的调试模式根本行不通,因为设置断点、单步调试会导致线程阻塞,导致出现超时等各种无关错误。系统。 甚至威胁到整个系统的正常运行。

在调试模式下,断点停止后,会自动快速取出调用堆栈、各层局部变量以及用户设置的表达式的值,然后执行断点。 断点需要用户主动才能再次生效。 此模式最大限度地减少调试操作对运行系统的影响。

获取局部变量首先是直接使用jdi接口实现的。 为了给用户提供足够的断点触发信息,每个局部变量的成员都会被递归取出。 当设置为递归四级时,jdi的性能问题就暴露出来了,断点触发后停顿时间长达数秒。 因此,我们扩展了jdwp,添加了FSAB,采用直接批量获取数据的方法。

此外,我们还提供了两个更独特的功能。 一种是基于调试模式的“多人在线调试”,允许用户创建或订阅自己感兴趣的断点,当断点触发时,会将相关信息推送给订阅该断点的用户。 这样,多个用户就可以调试同一个运行时系统,而不会互相干扰。 另一种是自动追踪用户关心的数据。 第一个断点触发后,通过FSAB获取局部变量成员数据时默认是广度遍历。 用户界面上最终显示的是一个可以展开的树,用户可以展开它来查看自己关注的成员。 此时系统会记录用户关注的是哪个会员,下次触发断点通过FSAB获取数据时,会根据用户关注的会员路径进行深度遍历。 获取更多用户关注的路径上的数据,而忽略其他分支。

顾名思义,就是热函数分析。 热点函数的统计分析是性能分析中非常重要的工作。 因为根据现有的经验,程序运行过程中的大部分时间都花在少量的代码上。 为了优化工作的投入产出比,首先要考虑优化程序中的热点功能。

热点函数统计有两种方法,一种是插桩法,一种是采样法。 前者的一个例子是 gprof,它通过在编译过程中添加特殊的编译选项,在所有函数的入口和出口处插入存根。 运行时,在存根函数中记录时间等信息,并输出到结果文件中。 之后就可以得到每个函数的精确调用次数、执行时间以及整个函数调用关系图。 缺点是需要重新编译,程序运行后才能得到结果。 后者的一个例子是perf,它通过高频定时中断来中断正在运行的程序,并在中断处理中记录当前正在运行的程序的上下文信息。 这样的结果显然是统计结果。 如果采样频率不够高(出于性能原因,一般不会特别高),有可能漏掉一些频繁执行的代码,但每次执行时间都比采样间隔短。 但其优点是无需重新编程,可随时开关,立即得出结果。

采用是后一种方法。 因为在用户态程序中,不能像perf那样使用硬件中断,而是使用定时信号(linux系统提供了定时器)。 我们提供了两个 jmx 命令,start 和 stop,它们实际上启动和停止计时器。 回到Java调用栈中定时器信号的处理函数。

回溯Java调用栈,使用了一个非标准的接口(据说商业软件也使用这个接口)。 通过调用该接口,可以获取当前被定时器信号中断的线程的Java调用堆栈,而无需等待JVM收集线程堆栈。 依靠收集堆栈的机制(例如通过调用)是有缺陷的,并且收集的热堆栈往往不精确。 在运行 JIT 代码的情况下,时间通常由 JIT 决定。 例如,在执行一些非常热的循环时,为了保证执行效率,JIT生成的代码不会插入轮询。 这种情况下,依赖机制就不会去这些热点。 有兴趣的读者可以在网上查找更多资料,或者直接阅读源码来了解其原理和用法。

结语

与传统的Java应用和调试相比,云计算环境在线系统有其相对独特的特点。 正如文中提到的,在内部,你可能需要处理运维,也可能需要与不同的系统进行接口,比如调试所需的源码需要与单片机(ent)对接等,我们需要将这些无缝连接起来,形成一站式解决方案,让攻城狮能够高效地发现、定位、解决问题。 从而提高整体生产效率。 从外部来看,对运行系统的性能要求非常高,对程序正常运行的影响必须在可以接受的范围内,这也是我们做了这么多定制的原因。 我们希望我们所获得的观察经验和实践经验能够为读者在类似环境中遇到类似问题时提供一些解决问题的思路。

关于作者:

宁志伟,绰号九驹,是一位技术专家。 2014年加入支付宝,目前就职于基础技术部JVM团队,从事JVM及相关调试调优工具的开发。 曾在华为工作九年,在中间件团队、操作系统团队、编译器团队从事嵌入式系统开发设计。

李嘉鹏,绰号寒泉子,高级研发工程师。 本科毕业四年多,他一直在支付宝工作。 一直从事监控系统、框架容器、性能分析系统的研发。 专注于性能分析系统的研发,年底加入JVM团队。

李三红 支付宝JVM专家。 曾任IBM多项目技术总监,2014年加入支付宝,目前负责基础技术部/相关开发工作。

本文选自《程序员电子版》2015年5月B期。 有关本期的更多文章,请查看此处。 有关自 2000 年创办以来的所有文章目录,请查看《程序员封面秀》。 欢迎订阅程序员电子版(包括iPad版、版、PDF版)。

值得一提的是,在Java诞生20周年之际,CSDN特别策划了Java专题,邀请Java领域的技术专家和知名书籍作者,从他们的角度讲述Java的技术变革和各个领域的技术实战。观点。 >>>(欢迎继续提交Java稿件(发邮件至)。

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON