10

在我的代码中,我执行了大量任务,每个任务都需要大量内存来临时存储数据。我有大约 500 个任务。在每个任务开始时,我为一个数组分配内存:

double[] tempDoubleArray = new double[M];

M 是一个很大的数字,取决于精确的任务,通常在 2000000 左右。现在,我做了一些复杂的计算来填充数组,最后我使用数组来确定这个任务的结果。之后, tempDoubleArray 超出范围。

分析表明构造数组的调用非常耗时。因此,我决定尝试重用该数组,将其设为静态并重用它。它需要一些额外的杂耍来确定数组的最小大小,需要额外通过所有任务,但它确实有效。现在,程序要快得多(从 80 秒到 22 秒执行所有任务)。

double[] tempDoubleArray = staticDoubleArray;

但是,我有点不知道为什么它的效果如此之好。我说在原始代码中,当 tempDoubleArray 超出范围时,它可以被收集,所以分配一个新数组应该不是那么难吧?

我问这个是因为理解它为什么起作用可能会帮助我找出实现相同效果的其他方法,并且因为我想知道在什么情况下分配会产生性能问题。

4

3 回答 3

7

仅仅因为可以收集某些东西并不意味着它会。事实上,如果垃圾收集器像它的收集器那样激进,你的性能会明显变差。

请记住,创建数组不仅仅是创建一个变量,而是创建N变量(N即数组中元素的数量)。重用数组是提高性能的一种物有所值的好方法,尽管您必须谨慎行事。

澄清一下,我所说的“创建变量”具体是指为它们分配空间并执行运行时必须执行的任何步骤以使它们可用(即将值初始化为零/空)。因为数组是引用类型,所以它们存储在堆上,这使得内存分配变得更加复杂。根据数组的大小(总存储空间是否超过 85KB),它将存储在普通堆或大对象堆中。与所有其他堆对象一样,存储在普通堆上的数组可以触发堆的垃圾收集和压缩(这涉及对当前正在使用的内存进行混洗以最大化连续的可用空间)。

于 2010-06-15T15:03:31.733 回答
1

一个答案可能是大型对象堆- 大于 85KB 的对象分配在不同的 LOH 上,该 LOH 的收集频率较低且未压缩。

请参阅性能影响部分

  • 有分配成本(主要是清除分配的内存)
  • 收集成本(LOH 和 Gen2 一起收集 - 导致 Gen2 中的大对象压缩)
于 2010-06-15T15:04:26.593 回答
0

在存在碎片的情况下分配大块内存并不总是那么容易。我不能肯定地说,但我的猜测是它必须进行一些重新排列才能为这么大的内存块获得足够的连续内存。至于为什么分配后续数组并不快,我的猜测是大块在 GC 时间和下一次分配之间变得碎片化,或者原始块从未 GCd 开始。

于 2010-06-15T15:05:39.463 回答