我正在尝试解决codeeval上的问题,但是遇到了使用太多内存的问题。在我的代码中,由于大量输入是不可避免的,因此有一个循环运行很多次(~10,000 ^ 2)。我注意到如果我运行循环并且在每次迭代时什么也不做,我总共使用大约6MB的内存和其他代码。但是,如果我在循环中添加一个简单的方法调用,它只调用一个返回false的函数,那么我的内存使用量会跳到20MB。
这是为什么?在函数调用完成后,是否不应为每个函数调用分配的内存被释放?
编辑:
完整的代码非常大,与post无关,但这个代码片段就是我所描述的。如果我不包含foo()
调用,我的代码作为一个整体运行使用6MB的内存。如果我包含foo()
调用,我的代码作为一个整体运行使用20MB的内存。我实际代码中的foo()
方法实际上是一样的(返回false),因为我想测试内存使用情况。
这是针对codeeval的编码挑战,所以问题应该可以用他们允许的任何语言解决,所以java应该没问题。
编辑:我已经重构了一些代码,以便我可以提取整个函数来向你们展示。这仍然产生前面描述的相同结果。产生奇怪行为的函数调用是are_friends()
。
ArrayList<ArrayList<Integer>> graph(String[] word_list) {
ArrayList<ArrayList<Integer>> adj_list = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < word_list.length; i++) {
adj_list.add(new ArrayList<Integer>());
}
for (int i = 0; i < word_list.length; i++) {
for (int j = i + 1; j < word_list.length; j++) {
if (are_friends(word_list[i], word_list[j])) {
adj_list.get(i).add(j);
adj_list.get(j).add(i);
}
}
}
return adj_list;
}
boolean are_friends(String a, String b) {
return false;
}
答案 0 :(得分:1)
如果我包含foo()调用,我的代码作为一个整体运行使用20MB的内存。
您应该注意对Java程序的内存使用情况的明确主张。
对于这些方法中的每一种,您可能会得到截然不同的测量结果。
内存使用的一个相关指标是将最大堆大小设置为-Xmx
,例如16 MB,并查看您的程序是否能够以其中一种或另一种形式完成无错误。请注意,这将仅限制堆而不是堆栈或JVM使用的任何其他支持内存区域。
在不限制堆的情况下,JVM可以自由地使用它认为合适的数量,保持大量垃圾以避免GC停滞。
答案 1 :(得分:0)
您遇到的问题是,现在它首先会调用一个方法,即foo()
,循环运行次数。
并且方法最终会出现在调用堆栈上,需要额外的时间进行处理,为了更深入的解释,您需要谷歌我害怕。
关键是当你将return false
置于bar()
内时,不会管理调用堆栈,因此使用更少的内存并可能更快。
我相信,在某些时候,如果在Hotspot JVM(默认值)上运行,JVM将内联您的foo()
方法调用,从而导致行为就像您直接在return false
中有一个bar()
。优化时,如果它完成,则取决于JVM参数和您的特定版本/系统。
然而即使已经优化,JVM也会声明内存。即使JVM不再使用内存 ,它也会拒绝将其返回到操作系统,因此您仍然可以观察到更高的内存使用量。
答案 2 :(得分:0)
在函数调用完成后,是否不应为每个函数调用分配的内存被释放?
没有。用于调用函数的内存进入堆栈。该线程在线程启动时分配,并且在线程退出之前不会被释放。
为什么会这样?
我相信您没有提供有关该问题的所有相关信息。我只是尝试在CodeEval上重现问题,并且在有和没有嵌套循环(500K以内)的情况下获得相同的内存使用。
值得注意的是,相同的代码会在运行到运行的CodeEval上产生不同的内存结果。我没有看到任何与您所看到的一样狂野的偏差,但显然涉及的因素多于代码。