1

为了将对象的实例传递给GC.AddMemoryPressure方法,使用了 C# 语言的哪些机制?

我通过 C# book在CLR中遇到了以下代码示例:

private sealed class BigNativeResource {
    private readonly Int32 m_size;
    public BigNativeResource(Int32 size) {
        m_size = size;
        // Make the GC think the object is physically bigger
        if (m_size > 0) GC.AddMemoryPressure(m_size);
        Console.WriteLine("BigNativeResource create.");
    }
    ~BigNativeResource() {
        // Make the GC think the object released more memory
        if (m_size > 0) GC.RemoveMemoryPressure(m_size);
        Console.WriteLine("BigNativeResource destroy.");
    }
 }

我不明白我们如何将一个对象的实例与它所增加的压力联系起来。我没有看到对象引用被传递给GC.AddMemoryPressure. 我们是否将增加的内存压力 (amp) 与对象相关联?

另外,我看不出有任何理由调用GC.RemoveMemoryPressure(m_size);. 从字面上看,它应该没有用。让我自己解释一下。有两种可能:对象实例之间存在关联,或者没有这种关联。

在前一种情况下,GC 现在应该m_size确定优先级并决定何时进行收集。因此,它绝对应该自行消除内存压力(否则 GC 对 GC 意味着什么remove an object while taking into an account the amp?)。

在后一种情况下,根本不清楚添加和移除放大器的用途。GC 只能使用定义为类实例的根。即GC只能收集对象。因此,如果对象和放大器之间没有关联,我看不出放大器如何影响 GC(所以我假设存在关联)。

4

3 回答 3

7

我不明白我们如何将一个对象的实例与它所增加的压力联系起来。

对象的实例通过调用将其添加的压力与对自身的引用相关联AddMemoryPressure。该对象已经与自己具有身份!添加和消除压力的代码知道是什么this

我没有看到对象引用被传递给 GC.AddMemoryPressure。

正确的。增加的压力与任何对象之间不一定存在关联,并且无论是否存在,GC 都不需要知道该信息以适当地采取行动。

我们是否将增加的内存压力 (amp) 与对象相关联?

GC 没有。如果您的代码这样做,那是您的代码的责任。

另外,我看不出有什么理由打电话给GC.RemoveMemoryPressure(m_size)

这样 GC 就知道额外的压力已经消失了。

我看不出放大器如何影响 GC

它会通过增加压力来影响 GC!


我认为对这里发生的事情存在根本性的误解。

增加内存压力只是告诉 GC 你知道一些关于内存分配的事实,而 GC 不知道,但与 GC 的动作有关。不要求增加的内存压力与任何对象的任何实例相关联或与任何对象的生命周期相关联

您发布的代码是一种常见模式:一个对象具有与每个实例相关联的额外内存,它会在分配额外内存时增加相应量的压力,并在释放额外内存时将其移除。但是不要求附加压力与一个或多个特定对象相关联。如果您在方法中添加了一堆非托管内存分配static void Main(),您可能会决定添加与其对应的内存压力,但没有与该额外压力相关联的对象。

于 2020-02-26T20:23:11.103 回答
2

AddMemoryPressure用于声明(此处强调)您在某处分配了合理大小的非托管数据。这个方法是运行时给你的礼遇。

该方法的目的是在您自己的责任下声明您在某个地方有非托管数据,这些数据在逻辑上绑定到某个托管对象实例。垃圾收集器有一个简单的计数器,只需将您指定的数量添加到计数器即可跟踪您的请求。

文档很清楚:当非托管内存消失时,您必须告诉垃圾收集器它已经消失了。

您需要使用此方法通知垃圾收集器非托管内存在那里,但如果关联对象被释放,则可以释放。然后,垃圾收集器能够更好地安排其收集任务。

于 2020-02-26T20:05:18.420 回答
2

这些方法的存在是为了让 GC 了解托管堆之外的内存使用情况。没有对象可以传递给这些方法,因为内存与任何特定的托管对象没有直接关系。代码作者有责任正确地通知 GC 内存使用的变化。

GC.AddMemoryPressure(Int64)

…运行时只考虑托管内存,因此低估了调度垃圾收集的紧迫性。

极端的例子是你有 32 位应用程序,而 GC 认为它可以轻松分配近 2GB 的托管 (C#) 对象。作为代码的一部分,您使用本机互操作分配 1GB。如果没有AddMemoryPressure调用,GC 仍然会认为它可以自由等待,直到您分配/取消分配大量托管对象......但是在您分配 1GB 托管对象的时候,GC 会进入奇怪的状态 - 它应该有整个额外的 GB 可以玩但是什么都没有了,所以它必须在那个时候争先恐后地收集内存。如果AddMemoryPressure使用得当,GC 将有机会在背景中更早地或在允许更短/更小的影响的点进行调整并更积极地收集。

于 2020-02-26T20:03:40.650 回答