简体   繁体   English

C#线程已经完成时如何导致内存泄漏?

[英]How do C# threads cause memory leaks when they already finished?

Here's my code: 这是我的代码:

using System;
using System.Collections.Generic;
using System.Threading;
namespace testt
{
    class MainClass
    {
        public static List<TestObject> testjobs = new List<TestObject> ();
        public static void Main (string[] args)
        {
            Console.WriteLine ("Hello World!");
            addTask (); //put a breakpoint here
            Thread.Sleep (5000);
            deleteObj ();
            while (true) {
                Console.WriteLine ("MAIN STILL EXISTS!");
                Thread.Sleep (1500);
                GC.Collect ();
            }
        }
        public static void addTask()
        {
            for (int i = 0; i < 10; i++)
            {
                testjobs.Add (new TestObject ());
                testjobs [i].Start ();
            }
        }

        public static void deleteObj()
        {
            for(int i=0; i<10;i++)
            {
                testjobs [0].dispose ();
                testjobs.RemoveAt (0);
            }
            Console.WriteLine (testjobs.Count);
        }

    }

    public class TestObject
    {

        private bool _isStopRequested;
        private Thread _thread;
        public void Start()
        {
            _thread = new Thread(ThreadRoutine);
            _thread.Start();
        }

        public void Stop()
        {
            _isStopRequested = true;
            if(!_thread.Join(5000))
            {
                _thread.Abort();
            }
        }

        public void dispose(){
            this.Stop ();
            this._thread.Abort ();
            this._thread=null;
        }


        private void ThreadRoutine()
        {
            //while(!_isStopRequested) //THIS CAUSES THE MEMORY LEAK!!!
            {
            Thread.Sleep (1);
            }
            Console.WriteLine ("THREAD FINISHED");
        }

        ~TestObject(){
            Console.WriteLine ("===================TESTOBJECT DESTROYED!!===============");
        }
    }
}

If you run it with the //while(!_isStopRequested) uncommented, the TestObject instances will not be destroyed, ie their destructor methods will not be called. 如果//while(!_isStopRequested)注释//while(!_isStopRequested)的情况下运行它,则不会破坏TestObject实例,即,不会调用其析构函数方法。

If you run it as is, then only 4-8 objects will be destroyed, not all 10 of them. 如果按原样运行,则将仅破坏4-8个对象,而不是全部10个对象。

Why does this happen when the threads have fully exited? 当线程完全退出时,为什么会发生这种情况? I checked with the Xamarin debugger and the threads were definitely stopped. 我检查了Xamarin调试器,发现线程肯定已停止。 If you put a breakpoint in Xamarin at addTask(); 如果您在Xamarin的addTask()处设置了断点; then you can see that 10 threads 那么你可以看到10个线程

My only explanation for this is that the thread somehow holds a reference back to their parent object TestObject instance even after they have finished. 我对此的唯一解释是,即使线程完成后,线程仍会以某种方式保留对其父对象TestObject实例的引用。 How can a thread hold a reference to their parent object when the thread has already finished? 线程完成后,线程如何保存对其父对象的引用?

Also, if I change Thread.Sleep(1) to Thread.Sleep(5000) , the TestObjects also stop being collected. 另外,如果我将Thread.Sleep(1)更改为Thread.Sleep(5000)TestObjects也将停止收集。

Also, as it is, only some TestObjects get collected whilst others don't. 同样,就这样,仅收集了一些TestObject,而没有收集。

Why do these things happen? 这些事情为什么会发生? How can I ensure that ALL the TestObjects get garbage collected by the time the deleteObj() function returns? 我如何确保在deleteObj()函数返回时所有TestObjects都得到垃圾回收?

EDIT: I just tested the exact same code in Visual Studio (.NET) and all of the objects were garbage collected regardless of whether if that line was commented out or not. 编辑:我只是在Visual Studio(.NET)中测试了完全相同的代码,并且不管是否注释掉该行,所有对象都被垃圾回收了。

Therefore I now consider this issue to be a Mono-specific problem and there was no memory leak to begin with. 因此,我现在认为此问题是Mono特定的问题,并且开始没有内存泄漏。

Finalizers are not deterministic. 终结器不是确定性的。 You cannot rely on them being called. 您不能依靠它们被调用。

If it is vitally important for your program to clean up the resource in question then you should be explicitly disposing of it, and not relying on a finalizer. 如果清理程序对资源至关重要,那么您应该明确地处理它,而不要依赖终结器。

If cleaning up the resource would be nice, but you don't really care all that much if the finializer gets to it or not, then you can choose to not explicitly dispose of the unmanaged resources. 如果清理资源会很好,但是无论最终处理程序是否成功,您都不在乎那么多,那么您可以选择不显式处置非托管资源。

Also note that making a managed object eligible for garbage collection doesn't necessarily mean that it will be garbage collected. 还要注意,使一个受管理对象有资格进行垃圾回收并不一定意味着它将被垃圾回收。 It means it can be collected whenever the collector feels like it. 这意味着只要收藏家喜欢可以收集它。

Finally, recognize that aborting a thread is another unreliable thing to do. 最后,认识到中止线程是另一不可靠的事情。 The thread do. 线程做。 There are a number of ways for a thread that has been requested to abort will not successfully do so, or where it will cause any number of different types of problems when it does. 有多种方法可以使请求中止的线程无法成功完成,或者在终止时会导致许多不同类型的问题。 You should avoid using Thread.Abort unless the thread in question was designed to be aborted, and you have a strong understanding of all of the many possible pitfalls of trying to reason about a program that could throw an exception between any two operations. 除非所讨论的线程被设计为中止,否则您应该避免使用Thread.Abort ,并且您对试图推理某个程序可能会在任何两个操作之间引发异常的所有可能的陷阱都有深刻的了解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM