我有一些使用以下配方的数据处理代码:
当块尽可能少时,最后一个阶段的效率最高,因此我希望第一阶段读入内存中尽可能多的数据。我可以通过查询
Runtime.freeMemory()
来做到这一点。
System.gc()
,或者Runtime.freeMemory()
返回的数字远小于我可以安全分配的内存量。
我听许多权威人士表示,明确致电
System.gc()
是一个坏主意。有什么办法可以避免这种情况吗?
即使您在检查拥有多少内存之前调用 System.gc(),也不能保证垃圾收集实际上会发生。我自己真的不会打扰,我会设置一个固定的块大小(最好通过属性或类似的配置)并始终使用它。如果程序的其余部分足够简单,则可以仅使用块大小加上固定数量的兆作为堆大小。如果由于其他原因你的程序的大小太不确定,你可以考虑并行运行两个程序并使用 IPC 机制。
当然,您的代码很可能需要对内存进行更细粒度的控制,但我谦虚地建议您使用错误的语言;或者至少是错误的运行时(那里有 RT java 产品,我认为它们更适合此类事情)。
如果这看起来不是最有用的答案,我很抱歉,但基本上我想知道您是否真的需要这个?
调用 System.gc() 是一个坏主意,很可能是因为它不能保证任何东西。
调用 System.gc() 是一个坏主意的真正原因是 JVM 最擅长知道运行 GC 的最佳时间1。如果您在其他时间调用 System.gc(),您可能会告诉 JVM 执行一些昂贵的操作以获得次优返回。
回到最初的问题,我认为最好的解决方案是不要尝试编写应用程序来再次猜测内存分配器。相反,对应用程序进行编码,使块大小为命令行参数/系统属性/其他内容,并手动调整块大小与 JVM 内存大小。您可能还想确保 JVM 初始内存大小和最大内存大小相同。
1 - “最佳”时间取决于 GC 的优化目的。如果您决定优化吞吐量,那么您希望在垃圾可能最多的时候运行 GC;即当堆已满时。另一方面,当您尝试满足暂停时间时,您需要确保 GC 跟上垃圾生成的速度。如果不能,JVM 可能不得不“停止世界”,以便 GC 能够赶上。 (细节我不太明白...)
缓存空闲内存的第一个值,重用它并让虚拟机完成工作。
非常好的时机。我今天早些时候问过this并得到了一些有用的答案,希望它有所帮助。
编辑:这并不能真正回答你的问题,但它指的是调用 System.gc() 不是一个好主意。
使用 JConsole 或类似的东西
调用 System.gc() 是一个坏主意,很可能是因为它不能保证任何东西。
如果您确实想确定 JVM 是否进行垃圾回收,您必须告诉它这样做。一种方式和JConsole一样,即通过JMX。
参见 http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html#local
使用 JMX 怎么样?特别是 MemoryMXbean:
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
另请检查 MemoryUsage 类。
JVM 工具接口 (jvmti) 有一个名为 ForceGarbageCollection 的方法。您可以编写一些 JNI 来调用它。
类似的东西
#include "jvmti.h"
#include "jni.h"
jvmtiEnv *jvmti;
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
(*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
return JNI_OK;
}
JNIEXPORT void JNICALL my_managled_function_name_that_is_entirely_too_long_to_be_easy_to_use (JNIEnv *env) {
error = (*jvmti)->ForceGarbageCollection(jvmti);
// you can trap the error if you want;
}
顺便说一句,这是一个坏主意。我仅使用此代码进行调试(以确保某些类(例如侦听器)不再具有可访问的引用。)
我敢打赌,虚拟机将在抛出内存错误之前收集所有可能的数据。