在我的代码中,我执行了大量任务,每个任务都需要大量内存来临时存储数据。我有大约500个任务。在每个任务开始时,我为内核分配内存 一个数组:
double[] tempDoubleArray = new double[M];
M是一个很大的数字,取决于精确的任务,通常大约2000000.现在,我做一些复杂的计算来填充数组,最后我使用数组来确定这个任务的结果。之后,tempDoubleArray超出范围。
分析表明构造数组的调用非常耗时。因此,我决定尝试重用数组,使其静态并重用它。它需要一些额外的杂耍 弄清楚阵列的最小尺寸,需要额外通过所有任务,但它确实有效。现在,程序更快(从80秒到22秒执行所有任务)。
double[] tempDoubleArray = staticDoubleArray;
然而,我有点不知道为什么这个效果如此之好。我想在原始代码中,当tempDoubleArray超出范围时,可以收集它,所以分配一个新数组应该不那么难吗?
我问这个是因为理解它的工作原理可能有助于我找出其他方法来达到同样的效果,因为我想知道在什么情况下分配会产生性能问题。
答案 0 :(得分:7)
仅仅因为可以收集的东西并不意味着它会。事实上,如果垃圾收集器像收集器一样具有攻击性,那么你的性能会更差。
请记住,创建数组不仅仅是创建一个变量,而是创建N
变量(N
是数组中元素的数量)。重复使用数组是一种很好的降低性能的方法,尽管你必须小心谨慎。
为了澄清,我的意思是“创建变量”具体是为它们分配空间并执行运行时所具有的任何步骤以使它们可用(即将值初始化为零/ null)。因为数组是引用类型,所以它们存储在堆上,这使得在内存分配方面生活变得复杂一些。根据数组的大小(无论它在总存储空间中是否超过85KB),它将存储在普通堆或大对象堆中。与所有其他堆对象一样,存储在普通堆上的数组可以触发堆的垃圾收集和压缩(这涉及在当前正在使用的内存中进行混洗以最大化连续的可用空间)。存储在大对象堆上的数组不会触发压缩(因为LOH永远不会被压缩),但它可能通过占用另一个大的连续内存块来触发过早的收集。
答案 1 :(得分:1)
一个答案可能是large object heap - 大于85KB的对象分配在不同的LOH上,不经常收集而不是压缩。
请参阅有关性能影响的部分
答案 2 :(得分:0)
在存在碎片的情况下分配大块内存并不总是那么容易。我不能肯定地说,但我的猜测是它必须做一些重新安排才能为这么大的内存块获得足够的连续内存。至于为什么分配后续数组的速度不快,我的猜测是大块在GC时间和下一次分配之间碎片化,或者原始块从来没有GCd开始。