Java 访问JDK 8 HotSpot JVM中字符串池内容的实用程序

Java 访问JDK 8 HotSpot JVM中字符串池内容的实用程序,java,string,jvm,jvm-hotspot,Java,String,Jvm,Jvm Hotspot,是否有任何实用程序或脚本(使用java或本机代码)可以查看JDK 8 HotSpot JVM中字符串池中存在的所有字符串的列表,而不会对JVM的性能产生很大影响 或者,当一个新字符串被添加到JVM中时,我可以让一个侦听器连接起来吗 谢谢, Harish您可以使用默认情况下包含在JDK中的HotSpot服务性代理轻松地创建这样的实用程序 import sun.jvm.hotspot.memory.SystemDictionary; import sun.jvm.hotspot.oops.Insta

是否有任何实用程序或脚本(使用java或本机代码)可以查看JDK 8 HotSpot JVM中字符串池中存在的所有字符串的列表,而不会对JVM的性能产生很大影响

或者,当一个新字符串被添加到JVM中时,我可以让一个侦听器连接起来吗

谢谢,
Harish

您可以使用默认情况下包含在JDK中的HotSpot服务性代理轻松地创建这样的实用程序

import sun.jvm.hotspot.memory.SystemDictionary;
import sun.jvm.hotspot.oops.InstanceKlass;
import sun.jvm.hotspot.oops.OopField;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

public class InternedStrings extends Tool {

    @Override
    public void run() {
        // Use Reflection-like API to reference String class and String.value field
        SystemDictionary dict = VM.getVM().getSystemDictionary();
        InstanceKlass stringKlass = (InstanceKlass) dict.find("java/lang/String", null, null);
        OopField valueField = (OopField) stringKlass.findField("value", "[C");

        // Counters
        long[] stats = new long[2];

        // Iterate through the String Pool printing out each String object
        VM.getVM().getStringTable().stringsDo(s -> {
            s.printValueOn(System.out);
            System.out.println();
            stats[0]++;
            stats[1] += s.getObjectSize() + valueField.getValue(s).getObjectSize();
        });

        System.out.printf("%d strings with total size %d\n", stats[0], stats[1]);
    }

    public static void main(String[] args) {
        // Use default SA tool launcher
        new InternedStrings().execute(args);
    }
}
运行工具:
java-cp$java_HOME/lib/sa jdi.jar:。InternetString

警告:这是一个在执行时暂停目标JVM进程的外部工具

还有几个可维护性代理示例

更新

如果您希望扫描所有字符串,而不仅仅是字符串池中的字符串,您可以使用类似的方法;只需将
getStringTable().stringsDo()
替换为
getObjectHeap().iterateObjectsOfKlass()

更新2

还可以使用JVMTI函数从Java进程内迭代Java堆。这将比服务性代理少一些干扰

jint JNICALL stringCallback(jlong class_tag, jlong size, jlong* tag_ptr,
                            const jchar* value, jint value_length, void* user_data) {
    wprintf(L"%.*s\n", value_length, value);
    return 0;
}

JNIEXPORT void JNICALL Java_HeapIterator_printStrings(JNIEnv* env, jclass cls) {
    jvmtiHeapCallbacks callbacks = {NULL, NULL, NULL, NULL, stringCallback};
    (*jvmti)->IterateThroughHeap(jvmti, 0, NULL, &callbacks, NULL);
}

完整的例子是。

Hmm。我想如果你做了一个堆内存转储,它会显示在那里。有没有比做heapdump更好的方法,因为我不需要所有其他堆数据,而且JVM可能会在这段时间内暂停?你到底为什么需要这样做?我计划定期监视JVM中使用的字符串并进行修改在后端,如果它们作为某个平台级组件的一部分与特定模式匹配,那么80-90%的字符串最终都会出现在字符串池中。对于来自外部的任何内容(用户输入、数据库调用),它可能接近0%。有人需要在字符串上调用
intern()
。谢谢。我将尝试使用JVMTI,因为它不会阻塞JVM。当JVM中创建了一个新字符串并访问它时,有没有办法注册一个监听器?正如原始问题所回答的,将这个问题标记为已回答。为监听器提出了一个单独的问题,即创建新字符串@watchout,
iteratethrowheap
不会阻止JVM。除文档外:
在执行此函数期间,堆的状态不会更改:没有对象被分配,没有对象被垃圾收集,并且对象的状态(包括保留值)不会更改。因此,执行Java编程语言代码的线程、尝试恢复执行Java编程语言代码的线程以及尝试执行JNI函数的线程通常会被暂停。
因此,这也会“暂停JVM”,因为必须确保对象不会更改。