gpt4 book ai didi

c# - 垃圾收集器在收集可访问对象的实例属性或字段时的行为不明确

转载 作者:行者123 更新时间:2023-12-02 12:18:00 24 4
gpt4 key购买 nike

直到今天我还认为可访问对象的成员也被认为是可访问的。

但是,今天我发现一种行为,当优化代码被选中或应用程序在中执行时,会给我们带来问题> Release模式。显然, Release模式也归结为代码优化。因此,代码优化似乎是造成这种行为的原因。

让我们看一下该代码:

 public class Demo
{
public Action myDelWithMethod = null;

public Demo()
{
myDelWithMethod = new Action(Method);

// ... Pass it to unmanaged library, which will save that delegate and execute during some lifetime

// Check whether object is alive or not after GC
var reference = new WeakReference(myDelWithMethod, false);

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);

Console.WriteLine(reference.IsAlive);
// end - Check whether object is alive or not after GC
}

private void Method() { }
}

我稍微简化了代码。实际上,我们正在使用我们的特殊委托(delegate),而不是 Action。但行为是一样的。编写这段代码时要考虑到“可访问对象的成员也被认为是可访问的”。但是,该委托(delegate)将尽快被 GC 收集。我们必须将它传递给一些非托管库,该库将使用它一段时间。

您只需将该行添加到 Main 方法即可测试演示:

var p = new Demo();

我可以理解这种优化的原因,但是在不创建另一个将使用将从某个地方调用的变量 myDelWithMethod 的函数的情况下,建议的方法是什么来防止这种情况? 其中一个选项是我发现的,如果我像这样在构造函数中设置 myDelWithMethod ,它就会起作用:

myDelWithMethod = () => { };

然后,直到拥有实例被收集后才会被收集。如果将 lambda 表达式设置为值,则似乎无法以相同的方式优化代码。

所以,我们很高兴听到您的想法。这是我的问题:

  • 可达对象的成员是否也被认为是可以联系吗?

  • 为什么在 lambda 表达式的情况下不收集?

  • 有什么建议的方法可以在这种情况下防止收集吗?

最佳答案

无论这听起来多么奇怪,JIT 都能够将对象视为不可访问,即使正在执行对象的实例方法(包括构造函数)。

以下代码是一个示例:

static void Main(string[] args)
{
SomeClass sc = new SomeClass() { Field = new Random().Next() };
sc.DoSomethingElse();
}
class SomeClass
{
public int Field;
public void DoSomethingElse()
{
Console.WriteLine(this.Field.ToString());
// LINE 2: further code, possibly triggering GC
Console.WriteLine("Am I dead?");
}
~SomeClass()
{
Console.WriteLine("Killing...");
}
}

可能打印:

615323
Killing...
Am I dead?

这是因为内联Eager Root Collection技术 - DoSomethingElse方法不使用任何 SomeClass 字段,因此在 LINE 2 之后不再需要 SomeClass 实例。

这发生在构造函数中的代码中。在 //... 将其传递给非托管库 行之后,您的 Demo 实例变得无法访问,因此它的字段 myDelWithMethod。这回答了第一个问题。

空lambda表达式的情况是不同的,因为在这种情况下,这个lambda被缓存在一个静态字段中,总是可访问的:

public class Demo
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Action <>9__1_0;
internal void <.ctor>b__1_0()
{
}
}

public Action myDelWithMethod;
public Demo()
{
myDelWithMethod = (<>c.<>9__1_0 ?? (<>c.<>9__1_0 = new Action(<>c.<>9.<.ctor>b__1_0)));
}
}

关于此类场景中的推荐方法,您需要确保 Demo 的生命周期足够长以涵盖所有非托管代码执行。这实际上取决于您的代码架构。您可以将 Demo 设为静态,或在与非托管代码范围相关的受控范围中使用它。这确实取决于。

关于c# - 垃圾收集器在收集可访问对象的实例属性或字段时的行为不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59361295/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com