Java 在运行的JVM中是否调用过此方法

Java 在运行的JVM中是否调用过此方法,java,jvm,coredump,Java,Jvm,Coredump,有没有办法确定在运行JVM的内部是否调用过方法。假设我有以下方法信息,我想知道它是否被调用过: "methodId": { "className": "InvokerParser", "filePath": "org/foo/commons/functors/InvokerParserformer.java", "methodName": "parser" } 如果应用程序在HotSpot JVM上运行,则可以使用HotSpot服务性代理获取

有没有办法确定在运行JVM的内部是否调用过方法。假设我有以下方法信息,我想知道它是否被调用过:

    "methodId": {
      "className": "InvokerParser",
      "filePath": "org/foo/commons/functors/InvokerParserformer.java",
      "methodName": "parser"
    }

如果应用程序在HotSpot JVM上运行,则可以使用HotSpot服务性代理获取有关给定方法的信息

下面是一个工具,用于检查该方法是否已在运行的JVM中被调用

import sun.jvm.hotspot.oops.InstanceKlass;
import sun.jvm.hotspot.oops.Method;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

public class CheckMethodCall extends Tool {
    private static final String className = "java/util/HashMap";
    private static final String methodName = "get";
    private static final String methodSig = "(Ljava/lang/Object;)Ljava/lang/Object;";

    @Override
    public void run() {
        boolean[] result = new boolean[2];

        VM.getVM().getSystemDictionary().classesDo(klass -> {
            if (klass.getName().asString().equals(className)) {
                Method method = ((InstanceKlass) klass).findMethod(methodName, methodSig);
                if (method != null) {
                    result[0] = true;
                    result[1] = method.getMethodCounters() != null &&
                        method.getInvocationCount() + method.interpreterInvocationCount() > 0;
                }
            }
        });

        if (!result[0]) {
            System.out.println("Method not found");
        } else if (result[1]) {
            System.out.println("Method has been called");
        } else {
            System.out.println("Method has NOT been called");
        }
    }

    public static void main(String[] args) {
        new CheckMethodCall().execute(args);
    }
}
它需要类路径中的
sajdi.jar
(随JDK8提供)


您可以使用Aspect/J。或者只是在方法中增加一个静态计数器。除非您使用探查器,否则不会有程序执行的运行跟踪。@ErwinBolwidt面向方面编程(AOP)是一个选项,您有如何实现这一点的示例参考吗?您可以控制代码中的方法吗?是的,我可以控制该方法很好,谢谢@apangin。这会捕获通过反射加载的类吗?@JauL Yes-VM中当前加载的任何类。但是请注意,类名、方法名和方法签名应该以JVM中描述的类文件格式指定。似乎
sa jdi.jar
只在JDK 9之前的版本中可用,并且从那时起就被封装了。有什么方法可以使用这个API吗?我只找到了
jhsdb
tool@JurajMartinka我用JDK 11版本的工具更新了答案。@apangin谢谢!以防万一(如果有人和我一样不熟悉模块系统),这就是使代理api对类路径上的所有代码可用的原因:
javac--add modules=jdk.hosts.agent--add exports“jdk.hosts.agent/sun.jvm.hosts.oops=all-UNNAMED”-add exports“jdk.hospot.agent/sun.jvm.hospot.tools=ALL-UNNAMED”--添加导出“jdk.hospot.agent/sun.jvm.hospot.runtime=ALL-UNNAMED”--添加导出“jdk.hospot.agent/sun.jvm.hospot.classfile=ALL-UNNAMED”CheckMethodCall.java
;通过
java
命令运行程序时也是如此。
java -cp $JAVA_HOME/lib/sa-jdi.jar:. CheckMethodCall <pid>
import sun.jvm.hotspot.oops.InstanceKlass;
import sun.jvm.hotspot.oops.Klass;
import sun.jvm.hotspot.oops.Method;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

public class CheckMethodCall extends Tool {
    private static final String className = "java/util/HashMap";
    private static final String methodName = "get";
    private static final String methodSig = "(Ljava/lang/Object;)Ljava/lang/Object;";

    @Override
    public void run() {
        Klass klass = VM.getVM().getClassLoaderDataGraph().find(className);
        if (klass == null) {
            System.out.println("Class not found");
            return;
        }

        Method method = ((InstanceKlass) klass).findMethod(methodName, methodSig);
        if (method == null) {
            System.out.println("Method not found");
            return;
        }

        boolean called = method.getMethodCounters() != null &&
                method.getInvocationCount() + method.interpreterInvocationCount() > 0;
        System.out.println("Method " + (called ? "has been" : "has NOT been") + " called");
    }

    public static void main(String[] args) {
        new CheckMethodCall().execute(args);
    }
}