【发布时间】:2013-10-22 15:58:41
【问题描述】:
我正在使用 .Net GC(仅用于教育目的),我对它的行为感到惊讶。
我已经构建了一个基本程序:
- 创建一个对象
- 显示其单个字段的地址及其当前代
- 运行一个集合
- 第二次显示地址和代
- 运行第二个集合
- 第三次显示地址和代
我预计地址和代每次都会改变,因为对象从一代提升到下一代,并从一个内存区域移动到下一个。
这对于在每个集合中递增的代是正确的,但字段的地址始终相同。
这是 IL 代码:
.assembly GCGenerationsSample { }
.assembly extern mscorlib { }
.class A extends [mscorlib]System.Object
{
.field int32 n;
.method instance void .ctor()
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
}
.method static void Main()
{
.entrypoint
.locals (class A)
newobj instance void A::.ctor()
stloc.0
// Display address of field "n"
ldloc.0
ldflda int32 A::n
call void [mscorlib]System.Console::WriteLine(int32)
// Display generation of object
ldloc.0
call int32 [mscorlib]System.GC::GetGeneration(object)
call void [mscorlib]System.Console::WriteLine(int32)
// Run a full GC
call void [mscorlib]System.GC::Collect()
// Display address of field "n"
ldloc.0
ldflda int32 A::n
call void [mscorlib]System.Console::WriteLine(int32)
// Display generation of object
ldloc.0
call int32 [mscorlib]System.GC::GetGeneration(object)
call void [mscorlib]System.Console::WriteLine(int32)
// Run a full GC
call void [mscorlib]System.GC::Collect()
// Display address of field "n"
ldloc.0
ldflda int32 A::n
call void [mscorlib]System.Console::WriteLine(int32)
// Display generation of object
ldloc.0
call int32 [mscorlib]System.GC::GetGeneration(object)
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
还有输出:
39070896
0
39070896
1
39070896
2
要么我错过了什么,要么做了一些愚蠢的事情,或者两者兼而有之(很有可能)。
欢迎提出任何想法。
【问题讨论】:
-
这不是它的工作方式。将临时段中的对象提升到下一代是通过将段移动到下一代来完成的,而不是对象。这只需要一个简单的指针更改,无需移动所有对象。通过回收旧段或分配新段,将段替换为新段。
-
@HansPassant:再次感谢您的洞察力 Hans。 :) 那么这意味着一代不是连续的内存区域而是一个虚拟区域,可能是不连续的,是段聚合的结果?并且我猜只有在压缩时才会物理移动对象?
-
它只是一块虚拟机空间,由 VirtualAlloc() 分配。只有 gen#2 是“不连续的”,即曾经是 gen#0 和 gen#1 段的段的集合。
-
gen#0 段大小是动态调整的,从 2MB 开始。空虚的一代不会得到提升。
-
@HansPassant:好的,所以在未来的某个时候它的大小将收敛到一个段的大小,避免从这一点开始任何浪费?再次感谢汉斯花时间分享。请添加一个答案,以便我接受。 :)
标签: .net garbage-collection clr cil il