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个内存分配.
| 归档时间: |
|
| 查看次数: |
1843 次 |
| 最近记录: |