常见问题

本文介绍持续剖析使用过程中的常见问题。

开启持续剖析后页面没有数据

  1. 检查相关配置是否正确,设置的网段是否包含了应用实例对应的IP地址。

  2. 若使用了3.1.4之前版本的探针,由于Alpine基础镜像的兼容性问题可能导致剖析引擎失效(该问题在3.1.4版本中已修复)。为确保功能稳定性及数据完整性,建议升级探针3.1.4及以上版本探针。

    说明

    验证是否使用了Apline Linux基础镜像的操作,请参见其他操作

  3. 持续剖析基于开源Async Profiler增强后对应用进行数据采集,当前暂不支持多Async Profiler同时挂载。如果应用同时使用了Pyroscope探针提供的持续剖析功能可能会出现启动失败。

  4. 将持续剖析页面的查询时间向后调整8小时,查看是否存在数据,如果有数据,可能是应用所在时区为0时区,所以数据写入时间比东八区(UTC+8)晚了8小时。

    解决方案:为应用添加环境变量将时区调整到UTC+8。

    说明

    建议先在持续剖析页面使用当前Pod的名称进行过滤,避免因过去8小时容器重启过多,导致看错信息。

    Key:JAVA_TOOL_OPTIONS,Value:-Duser.timezone=GMT+8

    该问题已经在 4.1.10 版本修复,如果确认是该问题,您也可以直接升级探针4.1.10及以上版本。

  5. 应用自身是否有挂载其他Async Profiler动态库,检查方式如下:

    1. 执行以下命令,其中[pid]需换成业务进程的pid信息。

      lsof -p [pid] | grep libasync
    2. 如果结果中有类似如下非阿里云的动态库,则是应用自身使用了Async Profiler动态库导致跟ARMS不兼容,此时需要移除自身动态库后才可以继续使用ARMS相关功能。

      /home/admin/xxx/.default/temp/libasyncProfiler1309163652530490111.so

新版持续性能剖析数据计算存储费用变化原因

导致上报量变化的主要原因:

  • 持续性能剖析是全新的产品能力,无缝兼容原有持续剖析能力,支持多实例聚合查询,线程级别的分析,引入Copilot智能化火焰图分析解读等能力。

  • 存储结构变化,为了满足用户对剖析数据的二次消费需求,剖析数据的存储介质将从原来内置的OSS中迁移至用户名下的SLS中(SLS Project:proj-xtrace-<encode>-<region-id>,SLS Logstore:logstore-profiling),这将导致剖析数据量有一定变化。

数据查询支持的时间跨度

持续性能剖析数据支持存储7天,您可以查询7天内的剖析数据。

堆内存监控的内存占比和持续剖析中内存热点单位时间检测到的内存占比数据不一样,是否正常?

持续剖析只记录指定时段内的堆内存申请情况,并不是当前进程实际包含的总量。

CPU诊断有数据但内存诊断无数据

该问题一般出现在3.1.4及以后版本的探针中。

内存诊断功能无数据,一般都是由于使用了 Alpine 基础镜像导致的,Alpine 基础镜像为了控制体积而去除了 JDK 调试符号(debug symbols),进而影响了持续剖析能力的正常使用,建议在用户镜像中为 JDK 安装调试符(部分 JDK 版本缺乏对应的调试符包,会导致无法安装)或使用非 Alpine 的基础镜像。

说明

验证所在环境是否存在调试符信息的操作,请参见检查所在环境JDK是否包含调试符信息

代码热点无数据或数据不符合预期

  1. 暂不支持对使用了JDK(包括 Alibaba Dragonwell JDK)中虚拟线程或类似技术的相关应用进行热点剖析。

  2. SkyWalking协议暂不支持使用代码热点功能。您可以在调用链的Span详情中的确认协议类型。

  3. 代码热点要求的最低探针版本为3.1.4,之前版本不支持代码热点功能。

  4. 低于4.2.1的探针版本,仅支持同步调用场景,并有一定概率可能采集不全,异步调用可能会出现没有数据(如使用Spring Cloud Gateway或者Undertow、Lettuce等,都会出现异步线程切换导致数据采集不准问题),4.2.1及以上版本探针对相关问题进行了优化,建议优先升级探针到相关版本进行使用。

  5. 仅按固定采样率采样的调用链支持代码热点功能,通过非固定采样率采集调用链时会引发较大的性能开销,如在调用链执行结束后才触发的错采样s9和慢采样s10(可以检查Spanattribute中的sample.reason字段进行判断),暂不支持代码热点功能。

    非固定采样率采集的调用链,建议在调用链分析页面通过仅含代码热点的Trace参数筛选包含代码热点的调用链,进行问题诊断。

    image

  6. 如果使用4.2.1及以上探针版本,但采集的耗时比外层Span统计的耗时低很多,这种情况需要检查一下应用使用了异步非阻塞NIO的框架(如Spring Cloud Gateway),这类框架在发送请求到下游时,如果下游数据没有准备好,不会阻塞线程,而是立即返回,让线程可以去做其他事情,从而提高线程的利用率。由于无线程实际执行,耗时瓶颈不是出现在本应用中,这种情况下代码热点采集耗时可能会比Span低很多,此时您可以重点排查下游应用请求处理或者网络是否存在瓶颈。

持续剖析性能开销是多少

  • 持续剖析功能经性能测试,在500 TPS场景下,在一般的Spring Web应用所有功能效果全部开启时, CPU开销增加5%左右,堆外内存开销增加50 M左右,GC以及请求延迟增加不明显。

  • 极端情况下,由于当前内存热点剖析没有做限流,如果应用申请内存非常频繁,可能会导致相关事件非常多,例如1分钟达到数万,可能会对P99延时造成影响。您可以先关闭内存剖析解决。

    该问题已经在 4.1.10 版本中修复,您可以直接升级探针4.1.10及以上版本。

应用中有JFR相关线程

  • 持续剖析功能会产生JFR线程,主要在4.1.10以下版本会存在JFR线程,4.1.10及以上版本不会再主动引入JFR相关线程。

  • 相关线程不会对应用产生性能瓶颈。

  • 动态关闭持续剖析开关后,该线程不会立即销毁,需要应用重启以后才会消失。

持续剖析影响应用启动时间

所有接入了ARMS 持续剖析的应用,如果没有JDK调试符号,且Classloader加载的类的方法较多,都可能影响启动速度(显著变慢)。但在应用启动完成后,对应用运行性能没有影响。

该问题已经在 4.2.1 版本修复,不会影响应用启动,您可以升级探针到相关版本。

火焰图中总内存超过实际配置的内存上限

持续剖析中展示的数据跟机器没有必然关系,该数据是分析的一段时间内,堆内存分配的内存空间大小,因为有垃圾回收,所以比实际机器实际配置的内存大是符合预期的。

OpenJ9 JDK接入失败

ARMS 持续剖析暂不支持IBM OpenJ9,使用相关JDK可能会出现接入失败并报错,建议更换到OpenJDK或者Oracle JDK。

Span中的代码热点数据不全

代码热点是根据一定时间间隔对调用链中的线程进行方法栈采集,对代码热点火焰图中缺失的方法,请检查其耗时是否小于500ms,如果小于500ms,可能有一定概率采集不到,这种情况不影响相关慢调用链诊断,因为耗时短的方法不会是耗时瓶颈。

火焰图中为什么存在.GC_active的栈

.GC_active 代表火焰图采集数据时,受到GCStop the World(即GC过程暂停所有Java业务线程的执行)过程影响,导致相关业务线程被暂停。如果在代码热点中出现,表示该次请求的耗时一部分是由于GC暂停导致的。

火焰图中为什么存在unknown

AlpineJDK无调试符号,类加载器中方法较多时,加载持续剖析可能会导致应用变慢甚至超时,因此,目前从在4.2.1版本探针开始,会检测加载耗时,默认150ms,超过会停止一些耗时步骤加载,导致出现部分方法符号信息未正常被解析出现unknown。可以通过比如export AP_JMID_TIME_LIMIT=500(单位毫秒)参数进行阈值调整缓解现象。

火焰图中为什么存在.no_Java_frame

一般是由于使用了Alpine基础镜像,Alpine基础镜像为了控制体积而去除了JDK调试符号(debug symbols),导致JDK里面的C++线程中的方法栈无法识别出函数名字,只能显示为no_Java_frame,由于这些方法栈主要是非Java的线程执行信息,一般常见如VM Thread或者JIT编译器线程,如果no_Java_frame相关内容占比不高,可以忽略,重点观察其他Java方法栈信息做性能分析,如果no_Java_frame相关内容占比较高,建议在基础镜像中为JDK安装调试符(部分JDK版本缺乏对应的调试符包,会导致无法安装)或使用非Alpine基础镜像。

火焰图中为什么出现other

问题现象

如下图所示火焰图中出现other项。

火焰图other选项

问题原因

火焰图中出现other项是正常的。火焰图是一棵树,当节点数较多时不便于从图中提取关键信息,因此ARMS通过一些方法对火焰图中的节点进行收敛,将一些重要性相对较低的节点并入到other项中。

parse lib sigsegv handler installed日志打印

该日志是ARMS探针打印的无用日志,仅在开启持续剖析功能后才会打印,对应用运行过程无影响,另外ARMS会在将来的新版本中关闭相关日志打印。

perf_event_open被限制导致的No access to perf events报错问题

问题现象

Async-Profiler进行CPU Profiler依赖perf_event_open的系统调用,但因为Linux kernelSyscall安全策略(seccomp)控制,可能会禁止进程调用特定Syscall。

错误提示如下:

[ERROR] Failed to execute 'start,jfr=0,event=cpu,interval=11ms,alloc=512k,file=/tmp/cpc-async-profiler-7729534006755968198.jfr'
[ERROR] Failed to start Continuous Profile Collector
java.lang.RuntimeException: java.lang.IllegalStateException: No access to perf events. Try --fdtransfer or --all-user option or 'sysctl kernel.perf_event_paranoid=1'

解决方案

  • Docker环境:执行以下命令运行容器。如需配置更精细化的系统调用控制,请参见官方文档

      docker run --security-opt seccomp=unconfined  XXX
  • Kubernetes环境:配置特权容器参数privileged: true,特权容器始终保持为Unconfined

    如需配置更精细化的系统调用控制,请参见官方文档

No AllocTracer symbols found. Are JDK debug symbols installed? 报错问题

如果Java进程运行在容器环境,出现以上报错或者该功能无数据,一般都是由于使用了Alpine基础镜像导致,Alpine基础镜像为了控制体积而去除了JDK调试符号(debug symbols),影响持续剖析能力的正常使用,建议参考本文缺少调试符导致内存热点采集失败的解决办法进行处理。

perf_event mmap failed...报错问题

问题现象

此错误一般出现在JVM的标准输出中。持续剖析功能进行CPU热点采样时,会同时采集Native(Linux Kernel + JVM + C/C++)以及Java栈,采集Native栈需要对Java中每个线程的perf_eventfd进行MMap,Linux内核中限制了进程perf_event相关的MMap的总内存大小(默认516 K Bytes)。当Java中线程数较多时,会触发限制并在Java标准输出中打印警告信息perf_event mmap failed...。出现这个告警信息,对Java的运行没有副作用,对业务也没有影响,实际的影响是火焰图中看不到Native的栈。一般来说定位CPU热点问题时,只看Java方法栈就够了,您可以忽略此告警。

解决方案

如果想消除这个错误信息,可以执行以下步骤:

  1. 在宿主机上执行以下命令。

    echo 1028 > /proc/sys/kernel/perf_event_mlock_kb

    默认阈值是516,可以逐渐增加,直到不出现告警,该值最好满足8*N + 4,N是自然数。例如516 = 512 + 4, 1028 = 1024 + 4。

  2. 重启Docker,即可消除错误。

其他操作

检查所在环境JDK是否包含调试符信息

缺少调试符信息将会导致内存热点开启失败,从而无法采集到内存热点数据。

  • 通过探针相关日志判断是否缺少调试符信息。

    在探针安装目录下的logs目录中检查是否有cpc.log日志文件(部分老版本探针在logs目录下,部分版本探针在logs/arms_log目录下),如果日志中包含关键字No AllocTracer symbols found. Are JDK debug symbols installed? ,则表示缺少调试符。

    image

  • 通过命令检查环境是否缺少调试符信息。

    • 进入JDK安装路径。

      可通过which java命令或echo$JAVA_HOME找到所在环境的JDK安装路径。

    • JDK安装路径最外层执行以下命令找到libjvm.so文件所在路径。

      find ./ -name "*libjvm.so*"
    • 执行以下命令检查插件是否被剥离调试符,/path/to/需替换成实际路径。

      file /path/to/libjvm.so

      如果返回not stripped,表示所在环境JDK包含调试符;如果返回stripped,表示不包含调试符。

      image

缺少调试符导致内存热点采集失败的解决办法

方法一:将JDK升级到JDK 11+

JDK 11+相关实现有调整,不再依赖于调试符信息。

方法二:更换基础镜像

可以在Dockerhub中,通过 openjdk 关键字选择一些著名的JDK发行版(如eclipse-temurinibm-semeru-runtimesamazoncorretto等),然后在其中搜索未基于 Alpine Linux 构建的JDK镜像(如果自身有常用的JDK镜像库,也可以在库中自行查找替换),一般 Alpine Linux 构建的JDK镜像的 tag 中都有 alpine 相关关键字。

image.png

非 Apline Linux 基础镜像构建的 JDK 版本示例:

image.png

如果更换基础镜像以后,还是没有数据,可以查看本地cpc.log日志文件,检查其中是否包含No AllocTracer symbols found .Are JDK debug symbols installed?报错信息,该信息表示环境中仍然没有调试符,请更换其他基础镜像或者方法三:使用标准的Alpine LinuxJDK

方法三:使用标准的Alpine LinuxJDK

使用3.2.8及以上版本的ARMS探针,并在Dockerfile中声明Alpine LinuxJDK。

from Alpine:3.9
RUN apk add openjdk8