剖析 Java 应用的性能
本页面介绍如何修改 Java 应用以捕获性能剖析数据,并将该数据发送到您的 Google Cloud 项目。如需了解性能剖析的常规信息,请参阅性能剖析相关概念。
Java 的性能剖析文件类型:
- CPU 时间
- 堆(需要 Java 11 或 App Engine 标准环境,默认处于停用状态)
- 实际用时(不适用于 Java 8 App Engine 标准环境)
支持的 Java 语言版本:
- 基于热点的 JVM(包括 Oracle JDK 和一些 OpenJDK Build),适用于 Java 8、11 或更高版本。
支持的性能剖析代理版本:
- 支持代理的最新版本。通常,不支持发布超过一年的版本。我们建议您使用最近发布的代理版本。
支持的操作系统:
- Linux。只有使用
glibc
或musl
实现其标准 C 库的Linux 内核支持剖析 Java 应用的性能。如需了解针对 Linux Alpine 内核的特定配置信息,请参阅在 Linux Alpine 上运行。
支持的环境:
- Compute Engine
- Google Kubernetes Engine (GKE)
- App Engine 柔性环境
- App Engine 标准环境(需要 App Engine SDK 1.9.64 或更高版本)
- Dataproc(如需了解相关信息,请参阅为 Dataproc Spark 和 Hadoop 作业配置 Cloud Profiler。)
- Google Cloud 外部(如需了解其他配置要求,请参阅剖析在 Google Cloud 外部运行的应用的性能。)
启用 Profiler API
在使用性能剖析代理之前,请确保已启用底层 Profiler API。您可以使用 Google Cloud CLI 或 Google Cloud 控制台查看该 API 的状态以及在必要时启用该 API:
gcloud CLI
如果您尚未在工作站上安装 Google Cloud CLI,请参阅 Google Cloud CLI 文档。
运行以下命令:
gcloud services enable cloudprofiler.googleapis.com
如需了解详情,请参阅 gcloud services
。
Google Cloud 控制台
-
Enable the required API.
如果系统显示 API 已启用,则表示此 API 已经启用。如未显示,请点击启用按钮。
向服务账号授予 IAM 角色
如果您在 Google Cloud 资源上部署应用,并且使用的是默认服务账号,并且未修改向该服务账号授予的角色,则可以跳过本部分。
如果您执行以下任何操作,则需要向服务账号授予 Cloud Profiler Agent (roles/cloudprofiler.agent
) IAM 角色:
- 您使用的是默认服务账号,但修改了其角色授予。
- 您使用的是用户创建的服务账号。
- 您使用的是 Workload Identity,请向 Kubernetes 服务账号授予 Cloud Profiler Agent 角色。
您可以使用 Google Cloud 控制台或 Google Cloud CLI 向服务账号授予 IAM 角色。例如,您可以使用 gcloud projects add-iam-policy-binding
命令:
gcloud projects add-iam-policy-binding GCP_PROJECT_ID \
--member serviceAccount:MY_SVC_ACCT_ID@GCP_PROJECT_ID.iam.gserviceaccount.com \
--role roles/cloudprofiler.agent
在使用上一个命令之前,请替换以下内容:
- GCP_PROJECT_ID:您的项目 ID。
- MY_SVC_ACCT_ID:您的服务账号的名称。
如需了解详情,请参阅管理对项目、文件夹和组织的访问权限。
安装 Profiler 代理
Compute Engine
为 Profiler 代理创建安装目录,例如
/opt/cprof
:sudo mkdir -p /opt/cprof
从
storage.googleapis.com
代码库下载代理归档文件,然后将其解压缩至安装目录:wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \ | sudo tar xzv -C /opt/cprof
GKE
修改 Dockerfile
以便为 Profiler 代理创建安装目录,下载代理归档文件并将其解压缩到安装目录中。
Linux(基于 glibc
的 C 库):
使用以下安装命令:
RUN mkdir -p /opt/cprof && \
wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
| tar xzv -C /opt/cprof
Linux Alpine(基于 musl
的 C 库):
使用以下安装命令:
wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent_alpine.tar.gz \ | tar xzv -C /opt/cprof
柔性环境
如果您使用的是 Google Java 8 运行时基础映像或 Java 9 / Jetty 9 运行时基础映像,那么系统已预装 Profiler 代理,因此您无需执行其他步骤来安装该代理。
对于所有其他基础映像,您需要安装该代理。例如,以下 Dockerfile
包含使用 openjdk:11-slim
映像安装 Profiler 代理的说明,它定义了启动应用时要使用的默认参数:
FROM openjdk:11-slim
COPY . .
RUN apt-get update \
&& apt-get install wget \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/cprof && \
wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
| tar xzv -C /opt/cprof
CMD ["java", "-agentpath:/opt/cprof/profiler_java_agent.so=OPTION1,OPTION2", "-jar", "PATH_TO_YOUR_JAR_FILE"]
如需将此 Dockerfile
与 App Engine 柔性环境一起使用,您需要执行以下操作:
标准环境
如果您使用的是 Java 运行时环境,那么系统已预装 Profiler 代理,因此您无需执行其他步骤来安装该代理。对于 Java 11 及更高版本,该库已预安装在 /opt/cprof
中。
Google Cloud 外部
为 Profiler 代理创建安装目录,例如
/opt/cprof
:sudo mkdir -p /opt/cprof
从
storage.googleapis.com
代码库下载代理归档文件,然后将其解压缩至安装目录:wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \ | sudo tar xzv -C /opt/cprof
如需列出所有可供下载的代理版本,请运行以下命令:
gcloud storage ls gs://cloud-profiler/java/cloud-profiler-*
命令响应如下所示:
gs://cloud-profiler/java/cloud-profiler-java-agent_20191014_RC00.tar.gz gs://cloud-profiler/java/cloud-profiler-java-agent_20191021_RC00.tar.gz gs://cloud-profiler/java/cloud-profiler-java-agent_20191028_RC00.tar.gz
如需下载特定版本的代理,请将其网址传递给下载命令。例如,如需下载 2019 年 10 月 28 日构建的代理,您可以使用以下语句:
wget -q -O- https://storage.googleapis.com/cloud-profiler/java/cloud-profiler-java-agent_20191028_RC00.tar.gz \
| sudo tar xzv -C /opt/cprof
在初始化代理期间会记录代理的版本。
加载 Profiler 代理
如需剖析应用的性能,请像往常运行程序一样启动 Java,但要指定代理配置选项。请指定指向代理库的路径,然后您可以将选项传递到该库。
对于 App Engine 标准环境,系统会自动加载和配置该代理。如需详细了解如何配置和启动您的程序,请直接跳到启动程序。
代理配置
如需配置性能剖析代理,请在启动应用时包含 -agentpath
标志:
-agentpath:INSTALL_DIR/profiler_java_agent.so=OPTION1,OPTION2,OPTION3
在此表达式中,INSTALL_DIR
是性能剖析代理的路径,而 OPTION1
、OPTION2
和 OPTION3
是代理配置选项。例如,如果在前一个表达式中将 OPTION1
替换为 --cprof_service=myapp
,那么您可以将服务名称设置为 myapp
。对于选项数量或其排序方式,没有任何限制。下表列出了支持的配置选项:
代理选项 | 说明 |
---|---|
-cprof_service
|
如果应用不在 App Engine 中运行,您必须使用此配置选项来设置服务名称。
如需了解服务名称限制,请参阅服务名称和版本参数。
|
-cprof_service_version
|
如果希望能够使用 Profiler 界面按服务版本分析性能剖析数据,请使用此选项设置版本。如需了解版本限制,请参阅服务名称和版本参数。 |
-cprof_project_id
|
如果应用不在 App Engine 中运行,您必须使用此配置选项来设置服务名称。 如需了解详情,请参阅剖析在 Google Cloud 外部运行的应用的性能。 |
-cprof_zone_name
|
应用在 Google Cloud 中运行时,性能剖析代理通过与 Compute Engine 元数据服务通信来确定相应区域。如果性能剖析代理无法与该元数据服务通信,您需要使用此选项。 |
-cprof_gce_metadata_server_retry_count -cprof_gce_metadata_server_retry_sleep_sec
|
这两个选项共同定义 Profiler 代理在与 Compute Engine 元数据服务通信时使用的重试政策,
以收集您的 Google Cloud 项目 ID 和区域信息。 默认政策是最多尝试 3 次,每两次尝试之间会等待 1 秒。此政策足够满足大多数配置的需要。 |
-cprof_cpu_use_per_thread_timers
|
如需获得最准确的 CPU 时间性能剖析文件,请将此选项设置为 true。使用此选项会导致每线程开销增加。
默认值为 false。 |
-cprof_force_debug_non_safepoints
|
默认情况下,性能剖析代理会强制 JVM 为所有即时 (JIT) 生成的代码生成调试信息,并生成所有安全点的调试信息。这样会使 CPU 时间和堆性能剖析文件的函数和行级位置信息最准确,但会产生额外的代理开销。您可以通过将此选项设置为 false 来禁止生成 JIT 代码的调试信息。 默认值为 true。 |
-cprof_wall_num_threads_cutoff
|
默认情况下,如果应用中的线程总数超过 4096,则系统不会收集实际用时性能剖析文件。该限制可确保在收集性能剖析文件时,遍历该线程栈的费用降至最低。
如果您的服务运行的线程通常会超出 4096 个,并且您愿意以额外开销为代价收集性能剖析数据,请使用此标志来提升限制。 默认限制为 4096 个线程。 |
-cprof_enable_heap_sampling
|
如需为 Java 11 及更高版本启用堆性能剖析,请设置-cprof_enable_heap_sampling=true 。Java 10 及更低版本不支持堆性能剖析。堆性能剖析默认处于停用状态。 启用堆性能剖析时,默认情况下,采样间隔设置为 512 KiB。对于大多数应用而言,此间隔已足够,并且会产生低于 0.5% 的应用开销。支持从 256 KiB (262144) 到 1024 KiB (1048576) 的采样间隔。 例如,如需将采样间隔设置为 256 KiB(这样会使采样率加倍),请添加以下代理选项:
|
服务名称和版本参数
加载 Profiler 代理时,请指定服务名称参数和可选的服务版本参数,以对其进行配置。
凭借服务名称,Profiler 可以收集该服务所有副本的性能剖析数据。对于每个组合服务版本和区域中的每个服务名称,Profiler 服务将保证平均每分钟生成一个性能剖析文件的收集速率。
例如,如果您的服务有两个版本在三个区域中的副本上运行,那么 Profiler 平均每分钟将为该服务创建 6 个性能剖析文件。
如果您为副本使用不同的服务名称,那么对服务执行性能剖析的频率将超出必要频率,相应的开销也会更高。
选择服务名称时,应注意以下几点:
选择的名称应能清楚地表示应用架构中的服务。如果您只运行单个服务或应用,那么服务名称的选择没那么重要;但如果您的应用作为一组微服务运行,则服务名称的选择较为重要。
切勿在服务名称字符串中使用任何进程专用的值,例如进程 ID。
服务名称字符串必须与如下正则表达式相符:
^[a-z0-9]([-a-z0-9_.]{0,253}[a-z0-9])?$
建议使用 imageproc-service
这样的静态字符串作为服务名称。
服务版本是可选的。如果您指定服务版本,则 Profiler 可以汇总来自多个实例的性能剖析信息并将其正确显示出来。服务版本可用于在部署服务时标记不同的版本。Profiler 界面支持您按服务版本过滤数据,这样一来,您就可以比较新旧版本代码的性能。
服务版本参数的值是一个使用自由格式的字符串,但此参数的值通常看起来像版本号,例如 1.0.0
或 2.1.2
。
启动程序
Compute Engine
像往常运行程序一样启动 Java,然后添加代理配置选项:
java \
-agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-cprof_service_version=1.0.0 \
JAVA_OPTIONS -jar PATH_TO_YOUR_JAR_FILE PROGRAM_OPTIONS
GKE
修改服务容器 Dockerfile,以像往常运行程序一样启动 Java,然后添加代理配置选项:
CMD ["java", \
"-agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-cprof_service_version=1.0.0", \
"-jar", "PATH_TO_YOUR_JAR_FILE" ]
柔性环境
修改 app.yaml
配置文件以设置 PROFILER_ENABLE
环境变量。然后像往常一样启动程序:
env_variables:
PROFILER_ENABLE: true
如需了解详情,请参阅定义环境变量。
标准环境
Java 21 运行时环境
如果您不使用旧版捆绑服务,请使用以下任一方法修改 app.yaml
文件以指定 agentpath
标志,从而启用性能分析器收集功能:
-
设置
JAVA_TOOL_OPTIONS
环境变量:runtime: java21 env_variables: JAVA_TOOL_OPTIONS: "-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true"
-
使用
entrypoint
元素指定agentpath
:runtime: java21 entrypoint: java \ -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true \ Main.java
如果您使用的是旧版捆绑服务,请使用以下任一方法修改 appengine-web.xml
文件以指定 agentpath
标志,从而启用性能分析器收集功能:
-
设置
JAVA_USER_OPTS
环境变量:<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <env-variables> <env-var name="JAVA_USER_OPTS" value="-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true" /> </env-variables> </appengine-web-app>
-
设置
CPROF_ENABLE
环境变量:<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <env-variables> <env-var name="CPROF_ENABLE" value="-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true" /> </env-variables> </appengine-web-app>
-
使用
entrypoint
元素指定agentpath
:<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <entrypoint> java -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true </entrypoint> </appengine-web-app>
如果为收集配置了新的性能剖析文件类型,请务必在部署应用时指定新的服务版本。如需了解详情,请参阅为什么我没有特定性能剖析文件类型的数据?
代理日志记录
性能剖析代理可以报告 App Engine 柔性环境、Compute Engine 和 GKE 的日志记录信息。性能剖析代理支持以下日志记录级别:
0
:记录所有消息。默认日志记录级别。1
:记录警告、错误和严重消息。2
:记录错误和严重消息。3
:仅记录严重消息并停止应用。
如需使用默认日志记录级别将日志写入标准错误,请将 -logtostderr
附加到 -agentpath
配置。
如需将日志记录级别设置为仅记录错误和严重消息,请将 -minloglevel=2
附加到 -agentpath
配置。
例如,如需使错误消息和严重消息的日志记录能够写入标准错误,请将 -logtostderr
和 ‑minloglevel=2
附加到 -agentpath
配置:
java -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_service=myapp,-logtostderr,-minloglevel=2 \
-jar myApp.jar
问题排查
本部分列出了剖析 Java 应用性所特有的问题。有关常见问题的帮助,请参阅问题排查。
行为 | 原因 | 解决方案 |
---|---|---|
您启用了多个堆分析器,但没有性能剖析数据。 | 同时使用多个堆分析器会停用对 Java 的所有堆性能剖析支持。这是 JVM 的局限性。 | 启用 1 个分析器。 |
使用 Linux Alpine 运行
只有 Google Kubernetes Engine 配置支持适用于 Linux Alpine 的 Java 性能剖析代理。
如需安装适用于 Linux Alpine 的最新 Java 性能分析代理,请参阅安装 Profiler 代理。身份验证错误
如果您的 Docker 映像使用 Linux Alpine(如 golang:alpine
或者只是 alpine
)运行,您可能会看到以下身份验证错误:
connection error: desc = "transport: authentication handshake failed: x509: failed to load system roots and no roots provided"
请注意,您必须启用代理日志记录才能看到该错误。
该错误表示使用 Linux Alpine 的 Docker 映像没有默认安装 SSL 根证书。这些证书为性能剖析代理与性能剖析器 API 进行通信所必需。如需解决此错误,请将以下 apk
命令添加到您的 Dockerfile 中:
FROM alpine
...
RUN apk add --no-cache ca-certificates
然后,您需要重新构建并重新部署应用。