与垃圾收集语言一起使用时,哪种代码的CPU /内存效率更高?

时间:2015-01-09 17:18:13

标签: java c# garbage-collection

我有两个虚拟代码片段(让我们考虑它们是用Java或C#编写的,所有变量都是本地的):

代码1:

int a;
int b = 0;

for (int i = 1; i < 10 ; i++)
{
    a = 10;
    b += i;

    // a lot of more code that doesn't involve assigning new values to "a"
}

代码2:

int b = 0;

for (int i = 1; i < 10 ; i++)
{
    int a = 10;
    b += i;

    // a lot of more code that doesn't involve assigning new values to "a"
}

乍一看,我会说两个代码都消耗相同的内存量,但代码1的CPU效率更高,因为它只创建和分配变量a一次。 然后我读到垃圾收集器是非常有效的,代码2将是更高的内存(和CPU?)效率:在循环中保持变量a使它属于Gen0,所以它将被垃圾收集之前变量b

因此,当与垃圾收集语言一起使用时,代码2效率更高。我是对的吗?

2 个答案:

答案 0 :(得分:40)

几点:

  • ints(和其他原语)永远不会在堆上分配。他们直接生活在线程堆栈上,&#34;分配&#34;和#34; deallocation&#34;是一个简单的指针移动,并发生一次(当输入函数时,并在返回后立即),无论范围如何。

  • 经常访问的
  • 原语通常存储在寄存器中以提高速度,无论范围如何。

  • 在你的情况下a(可能还有b以及整个循环)将被优化掉#34;,优化器足够智能化检测变量值更改但永远不会读取的情况,并跳过冗余操作。或者,如果存在实际查看a的代码,但未对其进行修改,则优化程序可能会将其替换为&#34; 10&#34;的常量值,这只是&#39; ll在任何引用a的地方显示内联。

  • 新对象(如果您执行类似String a = new String("foo")而不是int的操作)始终在年轻一代中分配,并且只有在它们存活后才会转移到旧的gen中几个小集合。这意味着,对于大多数情况,当一个对象在函数内部分配,并且从不从外部引用时,它将永远不会使它成为旧的,无论其确切的范围如何,除非您的堆结构迫切需要调整。

  • 正如评论中所指出的,有时VM可能决定直接在旧版本中分配一个大对象(对于java也是如此,而不仅仅是.net),所以上述观点仅适用于大多数案件,但不总是。但是,就这个问题而言,这并没有任何区别,因为如果决定在旧代中分配一个对象,那么无论如何都不考虑其初始引用的范围。

从性能和内存的角度来看,您的两个片段是完全相同的。但从可读性的角度来看,在尽可能窄的范围内声明所有变量总是一个好主意。

答案 1 :(得分:18)

在片段2中的代码实际执行之前,它最终会被转换为看起来像幕后片段1中的代码(无论是编译器还是运行时)。因此,两个片段的性能将是相同的,因为它们在某些时候会在功能上编译成相同的代码。

请注意,对于非常短暂的变量,它们实际上很可能根本没有为它们分配内存。它们可能完全存储在寄存器中,涉及0个内存分配。