剖析 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.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission. Learn how to grant roles. 如果系统显示 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/cprofLinux 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
   | 
  如果应用不在 Google Cloud中运行,您必须使用此配置选项来设置 Google Cloud 项目 ID。如需了解详情,请参阅剖析在 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_OPTIONSGKE
修改服务容器 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
然后,您需要重新构建并重新部署应用。