【问题标题】:Garbage collection promotes but does not move object垃圾收集促进但不移动对象
【发布时间】: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


【解决方案1】:

为了让它移动,您需要在被收集的幸存几代的对象之前分配一些东西,以便幸存的对象有一些空间可以压缩。

您的对象始终位于堆的开头,因此它不需要移动到任何地方。

【讨论】:

  • 是的,这是 Hans 描述的行为,我也通过压缩进行了验证(代码不在此处)。并且对象不在堆的开头,而是在其段的开头。 :)
猜你喜欢
  • 2010-11-08
  • 2011-07-16
  • 1970-01-01
  • 1970-01-01
  • 2019-09-04
  • 1970-01-01
  • 2012-07-09
  • 2011-10-12
  • 1970-01-01
相关资源
最近更新 更多