55.GC的主要耗时及地址变化

  1. 55.GC的主要耗时及地址变化
    1. 55.1 题目
    2. 55.2 深入解析
    3. 55.3 答题示例
    4. 55.4 关键词联想

55.GC的主要耗时及地址变化


55.1 题目

GC 的主要耗时在哪里?GC 后引用数据地址是否会改变?如何保证引用稳定?


55.2 深入解析

  1. GC 的主要耗时

    • 标记阶段(Mark):GC 扫描所有根对象(Root)并递归标记可达对象,这部分涉及遍历大量内存引用,开销较大。
    • 清扫阶段(Sweep):GC 清理未被标记的对象并回收内存,可能触发内存碎片整理,对大堆内存也有一定开销。
    • 压缩/整理阶段(Compact)(针对需要时):在压缩模式下,GC 会移动存活对象以合并空闲块,这一步需要复制和更新所有对象的引用,成本最高。
  2. GC 后对象地址是否改变

    • 小代和常规对象:在多数托管模式中,存活对象会被移动(Compact)到新位置,以减少碎片化,因此其内存地址会改变。
    • 大对象堆(LOH):默认情况下,LOH 对象不参与压缩,地址通常保持不变,除非启用了 LOH 压缩。
  3. 如何保证引用稳定

    • 降低GC的压力:尽量不GC或者降低GC的压力,通过避免闭包、装箱、频繁new等等GC优化手段可以保持稳定
    • 句柄/句柄表(Handles):.NET 使用句柄间接引用对象,GC 移动对象时只需更新句柄表,无需外部修改引用。
    • PIN(固定)对象:可以通过 GCHandle.Alloc(obj, GCHandleType.Pinned) 将对象固定在内存中,避免移动,但应谨慎使用以防碎片化。
    • 避免直接使用指针:若需与非托管代码交互,使用 GCHandlefixed 语句在作用域内固定对象,保证指针有效。

55.3 答题示例

“GC 的耗时主要集中在标记和清扫阶段,尤其是当需要压缩堆时,会移动存活对象,开销最大。大对象堆默认不压缩,所以 LOH 对象地址一般不变,而小对象会在 Compact 时改变地址。CLR 使用句柄表来间接引用对象,GC 更新句柄后,外部引用依旧有效;如果必须取直接指针,可以用 GCHandleType.Pinnedfixed 关键字将对象固定,防止移动。”


55.4 关键词联想

  • 标记-清扫(Mark-and-Sweep)
  • 堆压缩(Heap Compacting)
  • 大对象堆(LOH)
  • 句柄表(GCHandle)
  • 对象固定(Pinning)
  • 内存碎片化
  • 标记根对象(Roots)
  • 托管 vs 非托管互操作 (P/Invoke)


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏