垃圾收集器,调用和callvirt以及调试/释放代码模式执行差异

FSo*_*ou1 2 .net c# garbage-collection memory-management

我有一节课:

public class SomeClass {
    public int I;

    public SomeClass(int input) {
        I = input;
        Console.WriteLine("I = {0}", I);
    }

    ~SomeClass() {
        Console.WriteLine("deleted");
    }

    public void Foo() {
        Thread.Sleep(1000);
        Console.WriteLine("Foo");
    }
}
Run Code Online (Sandbox Code Playgroud)

这个程序:

class Program {
    static void Main(string[] args) {
        new Thread(() => {
                       Thread.Sleep(100);
                       GC.Collect();
                   }) { IsBackground = true }.Start();

        new SomeClass(10).Foo();

        // The same as upper code
        // var t = new SomeClass(10);
        // t.Foo();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我在调试模式下运行此代码时,我有下一个结果:

I = 10
Foo
deleted
Run Code Online (Sandbox Code Playgroud)

但是,当我将模式更改为Release时,结果更改为:

I = 10
deleted
Foo
Run Code Online (Sandbox Code Playgroud)

据我了解,有一个与区别callcallvirt:在优化开始释放模式,编译器看Foo的方法并不能找到任何引用SomeClass在此方法中,这就是为什么这种方法通过地址调用静态方法,以及垃圾收集器能够收集这个对象.

否则,如果我更改Foo方法(例如,添加Console.WriteLine(I)到此方法中),编译器将不会决定调用此方法,call并且应该通过指向实例的方法调用它,callvirt垃圾收集器将不会收集此对象.

你能否更清楚地解释一下,这里发生了什么(为什么GC可以收集对象,如果是这样,方法调用怎么样).

Jon*_*eet 5

我怀疑这是否真的什么关系callcallvirt.

我强烈怀疑这只是因为你没有使用任何领域SomeClass.Foo.当确定不会再次引用任何数据时,垃圾收集器可以自由地收集对象 - 因此没有代码会查看对象的任何引用或对象内的任何字段.

基本上,如果您编写终结器并且需要确保在该对象中的方法运行时未完成对象,则需要非常小心.你可以GC.KeepAlive(this)在方法的最后使用,作为一种方法,但它有点难看.如果可以的话,我会非常努力地避免需要终结器.我记不清上次写一篇了.(有关更多信息,请参阅Joe Duffy的博客.)

当有附加调试时,GC是什么它可以收集侵略性少-毕竟,如果你能打入任一点的调试和检查这是一个正在运行的实例方法的目标的任何对象的字段,去除垃圾收集这些物体的可能性.