在特定时间点释放Java内存

在特定时间点释放Java内存,java,memory,garbage-collection,Java,Memory,Garbage Collection,给定这段代码,我们加载大量数据,将其写入文件,然后运行exe void myMethod() { Map stuff = createMap(); //Consumes 250 MB memory File file = createFileInput(stuff); //Create input for exe runExectuable(file); //Run Windows exe } 在运行exe之前,释放stuff消耗的内存的最佳方法是什么?我们不再需要在内

给定这段代码,我们加载大量数据,将其写入文件,然后运行exe

void myMethod() {
    Map stuff = createMap(); //Consumes 250 MB memory
    File file = createFileInput(stuff); //Create input for exe
    runExectuable(file); //Run Windows exe
}
在运行exe之前,释放
stuff
消耗的内存的最佳方法是什么?我们不再需要在内存中使用它,因为我们已经将数据转储到一个文件中,以便输入到exe

在运行可执行文件之前设置
stuff=null
是最好的方法吗?

或者:

 runExecutable(createFileInput(createMap()));

请注意,
createFileInput
方法不会保留对
Map
实例的引用

将map对象设置为null是个好主意。甚至可以在将map设置为null后显式地执行System.gc()

但请记住,垃圾收集器只能按自己的计划工作,执行其中任何一项都不能保证垃圾收集器在其指定的时间点运行
但是,如果您的映射设置为null,那么在正常情况下,您将不会退出内存,而在最坏的情况下,当jvm达到最大堆大小时,它肯定会运行垃圾收集,因此内存将被释放。

stuff
设置为
null
肯定是必要的,但如果不强制垃圾收集,这是不够的。我假设
runExecutable
函数会在执行完成之前等待,因此我会在另一个线程中运行
System.gc()
,这样垃圾收集会在等待时发生。

因为映射内容是在方法内部本地声明的,因此,一旦该方法退出,它就应该有资格进行垃圾收集。

最好的答案可能是:什么都不做

void myMethod() {
    runExectuable(createFile()); //Run Windows exe
}

File createFile() {
    Map stuff = createMap(); //Consumes 250 MB memory
    return createFileInput(stuff); //Create input for exe
}
Java编译器非常聪明。在
createFileInput(stuff)
之后,它可以告诉您不再需要
stuff
,并重写代码以确保它是可访问的。如果希望100%确定,可以将其设置为null,但编译器可能已经为您完成了这项工作。将其设置为null并不能保证它是GC'd

试着先做一些分析,我想你会发现内存已经被释放了。天真地检查代码以找到您认为存在内存问题的地方很少会有好的结果。获取一些数据

编辑:注意编译器可以是
javac
或JIT编译器。两人都非常清楚这一点,并进行了各种你想象不到的优化。一句话:编写干净的代码,然后对其进行分析并从中进行优化


EDIT2:对于那些对JVM在内存方面的技巧感兴趣的人,我推荐这个博客(它很好地解释了这个特殊问题):

@jpalecek您认为System.gc()在内存不足时比JVM调用的任何其他垃圾收集()更能完成什么?自动垃圾收集就是这个意思。

如果您对@noah建议的内容不满意(什么也不做),或者代码运行在没有JIT或通用运行时优化的环境中,那么除了设置为null并调用System.gc()之外,一个更干净的替代方法就是分解代码,这样做

void myMethod() {
    File file = prepareFile(); //Prepare file for window exe
    runExectuable(file); //Run Windows exe
}

private File prepareFile() {
    Map stuff = createMap(); //Consumes 250 MB memory
    return createFileInput(stuff); //Create input for exe
}

这是一个更干净的代码,它允许东西一旦退出范围,就有资格收集。显式设置为null和调用gc()只是黑客行为。

我的第一个想法不是改变引用的范围或强制垃圾收集,而是可以减少内存需求吗

是否可以对功能进行重构,以便在写入文件时生成内容?例如,您可以将createMap和creatFile简化为计算地图一部分的内容吗

while (mapFiller.hasMore()) {
    Map map = mapFiller.getNextPartialMap();
    mapWriter.writePartialMap(map);
}
内存减少将是我的第一种方法,因为即使让JVM回收内存,也不意味着它一定会将内存释放回操作系统

另见:
并且

在运行exe之前将映射设置为null是否完成了相同的任务?是的,它完成了-在这两种情况下,您都放弃了指向巨大映射的最后一个引用。这使映射符合GC的条件。正如Fazal指出的,VM不需要立即启动GC循环,但可以保证,在VM抛出OOME之前,它会尽一切努力回收所有可用内存。所以,如果拿着这张地图让你抓到了OOMEs,那么清空它或者按照显示的方式构造代码会有帮助。你是在问一种释放内存的方法,以便gc可以收集它吗,或者关于强制gc在特定时间点运行的方法?我只是不希望exe内存不足,因为Java占用内存来保存映射。单独的可执行文件是否真的内存不足?如果JVM拥有大量内存,但在等待可执行文件完成时被阻塞,那么您的操作系统应该能够调出JVM,释放出您需要的任何内存。@Daniel,不是这样的。只是为了避免任何问题。在现代JVM中,您几乎肯定不需要将映射设置为null。请参阅@noah:在任何符合标准的JVM中,在其内容符合垃圾收集条件之前,您肯定需要将其设置为null。问题是,虽然JVM可能不会耗尽内存,但也不会因为另一个进程需要它而释放内存。@Michael,我希望您停止断言不正确的内容。该标准则相反,“编译器或代码生成器可能会选择将不再使用的变量或参数设置为null,以使此类对象的存储可能更快地被回收。”在createFileInput(stuff)之后,stuff不再使用,因此好的JIT或编译器会将其设置为null。-1:这是胡说八道。Java编译器和运行时都不会删除或初始化范围内变量引用的对象的GC。如果它这样做了,它将严重违反Java标准。@noah:我知道.NET CLR足够聪明,只要它能证明对象是正确的,它就可以收集对象