23

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

我的问题不是主观的,而是基于一个现实:在 Java 中可以强制 GC作为一个事实。我们每天使用的很多程序都是这样做的: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 VM 吗?

任何完整且可编译的示例都将受到欢迎。

4

4 回答 4

9

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% 的时间说:显式垃圾收集是万恶之源

于 2010-03-30T17:39:01.973 回答
5

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

#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

基于本教程:拥有你的堆:使用 JVMTI 迭代类实例

于 2015-09-22T13:40:55.827 回答
0

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

于 2011-11-30T19:41:51.870 回答
0

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

请看一下Eclipse:垃圾收集器按钮

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

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

于 2015-09-22T01:44:26.323 回答