1

I saw that I can print the current stack trace like this: Get current stack trace in Java.

But I would like to get every String.equals calls trace, and I don't know how to do it (I don't know where those calls are made since I use libraries).

I want to know when those calls are made to improve my code's performance.

Edit: According to the number of answers, maybe my question isn't clear: I want to trace every call of String.equals() in my Java program, I don't care about the method. ;)

Matheus Lacerda
  • 5,983
  • 11
  • 29
  • 45
Ltei
  • 425
  • 1
  • 6
  • 14

4 Answers4

2

I very much doubt that this is supported by many JVMs. One of the possible ways to intercept each method call is to instrument bytecode with Java Instrumentation API.

The issue is that classes mainly can be instrumented at the moment of class being loaded. So while the approach I suggest works well for you application classes, String class will be already preloaded by JVM. To modify loaded classes JVM should support either class retransform or class redefine. You can check this with next simple Maven project:

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.stackoverflow.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.stackoverflow.questions.stringequalscall.Agent</PreMain-Class>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

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

package com.stackoverflow.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/stackoverflow/questions/stringequalscall/AgentTest.java:

package com.stackoverflow.questions.stringequalscall;

public class AgentTest {

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

build JAR:

mvn clean install

And from your target folder run

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

If you JVM supports one of the mentioned loaded class manipulation methods then you can try to write your own Java agent that can add execution tracking to String#equals(String).

For me on Oracle Java 9 the output is:

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

BTW why do you believe that String#equals(String) may be the source of your performance problems?

P.S. At least it was fun writing simple Java agent.

Aleh Maksimovich
  • 2,622
  • 8
  • 19
  • I ued the profiler included in Android Studio and apparently String.equals takes 18,3% of the whole time! (And String.chatAt, which is probably called by String.equals takes 13.3% of the whole time) (I used exclusive time). Also I'm still trying to implement your solution for my problem, thank's ;) – Ltei Oct 25 '17 at 19:25
  • You know from your comment it seems that adjusting the profiling level and/or expecting methods [bottom up](https://developer.android.com/studio/profile/cpu-profiler.html#top_down_bottom_up) may be the right way to go. The agent is an overkill here. – Aleh Maksimovich Oct 25 '17 at 19:54
  • I updated my Android Studio and I now am using the new profiler, which is perfect for my issue, thank you Aleh! – Ltei Oct 26 '17 at 13:05
1

I would suggest to use a profiler. Most profilers offer a "hot spots" view where you would see the String.equals method and could open its backtraces, to see where the method calls are coming from.

An additional advantage over an isolated measurement is that you can assess it relative importance to other hot spots.

For example in JProfiler, the hot spot backtraces look like this:

enter image description here

If String.equals is not a hot spot, you can set the view filter at the bottom to java.lang.String to see all recorded methods of the String class.

Disclaimer: My company develops JProfiler

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
0

java.lang.Runtime.traceMethodCalls(boolean on) method enables/Disables tracing of method calls.

The following example shows the usage of lang.Runtime.traceMethodCalls() method.

            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!");
               }
            }

compile and run the above program, this will produce the following result −

Program is starting...

Enabling tracing...

Done!

this is for tracking all methods, I will update for specific one.

You can also try this : jcabi-aspects – Logging Java method executions

Annotate your methods with @Loggable annotation and every time they are called, your SLF4J logging facility will receive a message with the details of execution and the total execution time:

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

Something like this will appear in the log:

[DEBUG] #load('http://www.google.com'): returned "

  • Mmmm I don't really understand your answer, how can I use that to trace every String.equals calls when I don't know where they are? – Ltei Oct 24 '17 at 18:49
0

You are making an assumption. You are assuming String.equals is a major time-stealer.

The key to performance analysis is not to make any assumptions in advance, because then you are blind to letting the program tell you what takes time, which might be what you thought, but is probably something else.

Here's a simple example of how to let the program tell you what takes time.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135