【问题标题】:Java: How do you really force a GC using JVMTI's ForceGargabeCollection?Java:您如何真正使用 JVMTI 的 ForceGargabeCollection 强制 GC?
【发布时间】:2011-01-11 19:21:57
【问题描述】:

我不是在寻找通常的“您只能使用 System.gc() 在 Java 中提示 GC”的答案,这根本不是这个问题的意义所在。

我的问题不是主观的,而是基于一个现实:GC 可以在 Java 中被强制用于一个事实。我们每天使用的很多程序都可以做到这一点:IntelliJ IDEA、NetBeans、VisualVM。

它们都可以强制 GC 发生。

它是如何完成的?

我认为他们都在使用 JVMTI,更具体地说,是 ForceGarbageCollection(注意“Force”),但我该如何自己尝试呢?

http://java.sun.com/javase/6/docs/platform/jvmti/jvmti.html#ForceGarbageCollection

还要注意,这个问题不是关于“为什么”我想这样做:“为什么”可能是“好奇”或“我们正在编写类似于 VisualVM 的程序”等。

问题实际上是“如何使用 JVMTI 的 ForceGarbageCollection 强制 GC”?

JVM 是否需要使用任何特殊参数启动?

是否需要任何 JNI?如果有,具体是什么代码?

它只适用于 Sun 虚拟机吗?

欢迎提供任何完整且可编译的示例。

【问题讨论】:

    标签: java garbage-collection jvmti


    【解决方案1】:

    至少,NetBeans 使用 System.gc():http://hg.netbeans.org/main/annotate/9779f138a9c9/openide.actions/src/org/openide/actions/GarbageCollectAction.java(这是用于显示当前堆并让您启动 GC 的小按钮)。如果您点击该链接,您会看到它们显式运行终结器。如果您有一些可用的磁盘空间,并且想自己研究代码,可以通过 Mercurial 获得它:hg clone http://hg.netbeans.org/main/

    据我所知,“System.gc() 只是一个提示”教条源于对 JLS 和 JVM 规范的迂腐解释,它允许没有垃圾收集堆的 Java 实现。那,以及对JavaDoc的不完整阅读:

    调用 gc 方法表明 Java 虚拟机花费精力 朝着回收未使用的对象 为了让他们的记忆 目前占用可用于快​​速 重用。当控制权从 方法调用,Java 虚拟机 已尽最大努力收回 所有丢弃的对象的空间。

    阅读第二句:“尽最大努力回收空间”比“提示”强很多。

    也就是说,很少有理由致电System.gc()。向 Knuth 道歉:

    我们应该忘记内存管理,比如大约 97% 的时间:显式垃圾收集是万恶之源

    【讨论】:

    • 我不会将其称为基于迂腐解释的教条。大多数 JVM 可以简单地禁用对 System.gc() 的调用,因此您不能相信它可以做任何事情。很多时候你有一个写得很好的应用程序,其中 gc 工作很有趣,引入一个库,开发人员认为他可以通过调用 System.gc() 来“确保内存可用”,关闭它是返回的最佳方法你良好的 gc 性能。
    • 用户仍然可以控制它。对于热点虚拟机,您可以设置-XX:+DisableExplicitGC,也可以不设置。因此,只要您能够控制 JVM 标志,System.gc() 就相当可靠。该论点仅适用于无法控制这些标志/无法假设它将在哪个 VM 上运行的库编写者。这是一般情况(规范)与具体用法(所有参数由用户控制)的事情。
    • 应用程序编写者通常无法控制 JVM 标志。例外情况是应用程序封装在启动器中,以防止用户或系统管理员对其进行调整。这不是迂腐。事实上,该标志专门用于保护人们免受 Java 应用程序/应用程序编写者做愚蠢的事情。
    【解决方案2】:

    我构建了一个基本的 java 代理,允许调用 jvmti ForceGarbageCollection 函数:

    #include <stdlib.h>
    #include <stdio.h>
    #include <jvmti.h>
    
    
    typedef struct {
     jvmtiEnv *jvmti;
    } GlobalAgentData;
    
    static GlobalAgentData *gdata;
    
    JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
    {
      printf("load garbager agent\n");
      jvmtiEnv *jvmti = NULL;
    
      // put a jvmtiEnv instance at jvmti.
      jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
      if (result != JNI_OK) {
        printf("ERROR: Unable to access JVMTI!\n");
      }
    
      // store jvmti in a global data
      gdata = (GlobalAgentData*) malloc(sizeof(GlobalAgentData));
      gdata->jvmti = jvmti;
      return JNI_OK;
    }
    
    
    extern "C"
    JNIEXPORT void JNICALL Java_Garbager_forceGarbageCollection(JNIEnv *env, jclass thisClass) 
    {
      printf("force garbage collection\n");
      gdata->jvmti->ForceGarbageCollection();
    }
    

    这个代理是通过JNI调用的:

    class Garbager {
        public static void main(String[] args) {
            Garbager.garbageMemory();
        }
    
        static void garbageMemory() {
            forceGarbageCollection();
        }
    
        private static native void forceGarbageCollection();
    }
    

    在 MacOSX 上编译代理:

    clang -shared -undefined dynamic_lookup -o garbager-agent.so -I /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/include/darwin garbager-agent.cpp
    

    要启动Garbager

    java -agentpath:garbager-agent.so Garbager
    

    基于本教程:Own your heap: Iterate class instances with JVMTI

    【讨论】:

      【解决方案3】:

      是的,使用 JVMTI API 需要 JNI 接口代码,因为它是本机 API。 “本机”意味着您只能直接从本机(了解 c 或 c++)代码中调用它。所以如果你想从java调用这个API,你需要编写JNI代码来接口它。

      【讨论】:

        【解决方案4】:

        正如 Anon 所说,我们在 eclipse 中有类似的东西来显式运行垃圾收集器。

        请看Eclipse: Garbage Collector Button

        这似乎工作得很好。我建议你看看这个“运行垃圾收集器”按钮背后的代码并重用它。

        有些成员说它使用 System.gc() 但我无法确认。 Eclipse 专家可以在这里提供一些启示。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-18
          • 1970-01-01
          相关资源
          最近更新 更多