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

Bro*_*dow 7 c# java 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"
}
Run Code Online (Sandbox Code Playgroud)

代码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"
}
Run Code Online (Sandbox Code Playgroud)

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

因此,当与垃圾收集语言一起使用时,代码2更有效.我对吗?

Dim*_*ima 40

几点:

  • ints(和其他原语)永远不会在堆上分配.它们直接存在于线程堆栈中,"分配"和"释放"是指针的简单移动,并且发生一次(当输入函数时,并且在返回后立即),而不管范围如何.

  • 经常访问的基元通常也存储在寄存器中以提高速度,无论范围如何.

  • 在你的情况下a(也可能,b与整个循环一起)将被"优化掉",优化器足够聪明,可以检测变量值发生变化但永远不会读取的情况,并跳过冗余操作.或者,如果存在实际查看的代码a但不修改代码,则优化程序可能会将其替换为常量值"10",这样只会在a所引用的任何位置显示为内联.

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

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

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


Ser*_*rvy 18

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

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