与垃圾收集语言一起使用时,哪种代码的CPU/内存效率更高? 我有两个虚构的代码(我们认为它们是用java或C语言编写的,所有变量都是局部的):
代码1:与垃圾收集语言一起使用时,哪种代码的CPU/内存效率更高? 我有两个虚构的代码(我们认为它们是用java或C语言编写的,所有变量都是局部的):,java,c#,garbage-collection,Java,C#,Garbage Collection,代码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" } inta; int b=0; 对于(int i=1;i
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"
}
inta;
int b=0;
对于(int i=1;i<10;i++)
{
a=10;
b+=i;
//还有很多代码不涉及给“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"
}
intb=0;
对于(int i=1;i<10;i++)
{
INTA=10;
b+=i;
//还有很多代码不涉及给“a”赋值
}
乍一看,我想说这两个代码消耗的内存量相同,但代码1的CPU效率更高,因为它只创建和分配变量a
。
然后我读到垃圾收集器非常高效,代码2的内存(和CPU?)会更大。效率:将变量a
保留在循环中会使它属于Gen0,因此它会在变量b
之前进行垃圾收集
因此,当与垃圾收集语言一起使用时,代码2的效率更高。我说得对吗?在代码段2中的代码实际执行之前,它最终会被转换成代码段1中的代码(无论是编译器还是运行时)。因此,这两个代码段的性能将是相同的,因为它们将在某个时候编译成功能相同的代码 请注意,对于寿命很短的变量,实际上很可能根本没有为它们分配内存。它们很可能完全存储在寄存器中,涉及0内存分配。几点:
- int(和其他原语)永远不会在堆上分配。它们直接存在于线程堆栈上,“分配”和“解除分配”是指针的简单移动,并且只发生一次(当函数被输入时,以及在返回后立即发生),而不考虑范围
- 经常访问的原语通常存储在寄存器中,以提高速度,这与范围无关
- 在您的情况下,
(也可能,a
,以及整个循环)将被“优化掉”,优化器足够智能,可以在变量值更改时检测情况,但永远不会读取,并跳过冗余操作。或者,如果有代码实际查看b
,但没有对其进行修改,那么它很可能会被优化器替换为常量值“10”,它只会在引用a
的任何地方内联显示a
- 新对象(例如,如果您使用
而不是int)总是在年轻的一代中分配,并且只有在经过一些小的收集之后才转移到旧的一代中。这意味着,在大多数情况下,当一个对象被分配到一个函数内部,并且从未从外部引用时,无论其确切作用域是什么,它都不会被分配到旧的gen,除非堆结构迫切需要调整String a=New String(“foo”)
- 正如评论中指出的,有时候VM可能会决定直接在旧版本中分配一个大对象(java也是如此,而不仅仅是.net),因此上述观点只适用于大多数情况,但并不总是适用。但是,关于这个问题,这没有任何区别,因为如果决定在old gen中分配对象,则无论如何都不会考虑其初始引用的范围
从性能和内存的角度来看,您的两个代码段是相同的。不过,从可读性的角度来看,最好在尽可能狭窄的范围内声明所有变量 这里没有垃圾收集。重复重新分配
a
有什么意义?还请注意,这两个位是完全不同的(第二个位在循环外没有a
变量,第一个位有)。@T.J.Crowder显然这只是一个玩具示例;一般的想法是,将变量限定在一个循环内是否有意义,或者是否应该将它们拉到循环外(即使不在循环外使用)作为优化。@T.J.Crowder正如Servy所说:这是一个玩具样本来说明优化问题通常最好是给出一个至少具有模糊现实性的示例,而不是做一些你永远不会真正做的事情。要么是编译器,要么是jit?@TheLostMind是的,在堆栈中的某个地方。就这个问题而言,哪一个并不重要。我同意,它是如何发生的并不重要,从一个发行版到另一个发行版的变化:)编译器不会完全删除“int a=10”吗?(我知道这是一个玩具示例)如果没有任何编译器优化,并且代码完全按照原样编译,我猜代码2更糟糕不?@Dzyann在确切的示例中,给定变量在两种情况下都没有使用,因此在这两种情况下都将完全忽略。假设变量是从中读取的,这似乎是问题的意图,答案适用。无论如何,代码片段实际上仍然没有区别。对于当前的.NET实现,GC和大型对象将被分配到大型对象堆上,这实际上是Gen2(最高一代)。所以分配并不总是分配给最年轻的一代。@CodesInChaos这是一个很好的观点(java也是如此,而不仅仅是.net)。谢谢你指出!我已经更新了答案以反映这一点。@CodesInChaos:不,LOH实际上与Gen2不同。LOH根本不是分代压缩过程的一部分。@BenVoigt大对象被分配到不同的内存区域是对的。但它中的对象只会在完整(Gen2)GC期间被考虑收集GC.GetGeneration(新字节[100*1000])
为2。