获取 String.equals 次调用跟踪

Get String.equals calls trace

我看到我可以像这样打印当前堆栈跟踪: Get current stack trace in Java.

但我想获取每个 String.equals 调用跟踪,但我不知道该怎么做(我不知道这些调用是从哪里发出的,因为我使用的是库)。

我想知道何时进行这些调用以提高我的代码的性能。

编辑:根据回答的数量,可能我的问题没有说清楚: 我想在我的 Java 程序中跟踪每个 String.equals() 的调用,我不关心方法。 ;)

java.lang.Runtime.traceMethodCalls(boolean on) 方法 enables/Disables 跟踪方法调用。

以下示例显示了 lang.Runtime.traceMethodCalls() 方法的用法。

            public class RuntimeDemo {

               public static void main(String[] args) {

                  // print the state of the program
                  System.out.println("Program is starting...");

                  // start tracing for instructions
                  System.out.println("Enabling tracing...");
                  Runtime.getRuntime().traceMethodCalls(true);
                  System.out.println("Done!");
               }
            }

编译和运行上面的程序,这将产生以下结果-

程序正在启动...

正在启用跟踪...

完成!

这是为了跟踪所有方法,我会针对特定的方法进行更新。

您也可以试试这个:jcabi-aspects – 记录 Java 方法执行

使用@Loggable 注释对您的方法进行注释,每次调用它们时,您的 SLF4J 日志记录工具将收到一条消息,其中包含执行的详细信息和总执行时间:

            public class Resource {
              @Loggable(Loggable.DEBUG)
              public String load(URL url) {
                return url.openConnection().getContent();
              }
            }

日志中会出现这样的内容:

[DEBUG] #load('http://www.google.com'): 返回“

我非常怀疑许多 JVM 都支持这一点。拦截每个方法调用的一种可能方法是使用 Java Instrumentation API.

检测字节码

问题是 classes 主要可以在 class 加载时进行检测。因此,虽然我建议的方法适用于您的应用程序 classes,但 JVM 已经预加载了 String class。要修改加载的 classes,JVM 应该支持 class retransform 或 class redefine。您可以使用下一个简单的 Maven 项目来检查:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.Whosebug.questions</groupId>
    <artifactId>stringequalscall</artifactId>
    <version>0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <PreMain-Class>com.Whosebug.questions.stringequalscall.Agent</PreMain-Class>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

src/main/java/com/Whosebug/questions/stringequalscall/Agent.java:

package com.Whosebug.questions.stringequalscall;

import java.lang.instrument.Instrumentation;

public class Agent {

    public static void premain(String args, Instrumentation instrumentation) {
        System.out.println("Can redefine classes: " + instrumentation.isRedefineClassesSupported());
        System.out.println("Can retransform classes: " + instrumentation.isRetransformClassesSupported());
        System.out.println("String modifiable: " + instrumentation.isModifiableClass(String.class));
    }
}

src/main/java/com/Whosebug/questions/stringequalscall/AgentTest.java:

package com.Whosebug.questions.stringequalscall;

public class AgentTest {

    public static void main(String[] args) {
        System.out.println("Executing Agent test");
    }
}

构建 JAR:

mvn clean install

从您的 target 文件夹 运行

java -javaagent:stringequalscall-0.1-SNAPSHOT.jar com.Whosebug.questions.stringequalscall.AgentTest

如果您的 JVM 支持上述加载的 class 操作方法之一,那么您可以尝试编写自己的 Java 代理,它可以将执行跟踪添加到 String#equals(String).

我在 Oracle Java9 上的输出是:

Can redefine classes: false
Can retransform classes: false
String modifiable: true
Executing Agent test

顺便说一句,您为什么认为 String#equals(String) 可能是性能问题的根源?

P.S。至少编写简单的 Java 代理很有趣。

我建议使用分析器。大多数分析器提供 "hot spots" 视图,您可以在其中看到 String.equals 方法并可以打开其回溯,以查看方法调用的来源。

与孤立测量相比的另一个优势是您可以评估它相对于其他热点的重要性。

例如在 JProfiler 中,热点回溯如下所示:

如果String.equals不是热点,可以将底部的查看过滤器设置为java.lang.String,查看Stringclass.[=15=的所有记录方法]

免责声明:我公司开发JProfiler

你在做一个假设。你假设 String.equals 是一个主要的时间窃取者。

性能分析的关键是不要事先做任何假设, 因为这样你就不会让程序 告诉你什么需要时间,这可能是你的想法,但可能是别的东西

Here's a simple example 如何让程序告诉你什么需要时间。