Note that there are some explanatory texts on larger screens.

plurals
  1. POMemory leak while using Threads
    primarykey
    data
    text
    <p>I appear to have a memory leak in this piece of code. It is a console app, which creates a couple of classes (WorkerThread), each of which writes to the console at specified intervals. The Threading.Timer is used to do this, hence writing to the console is performed in a separate thread (the TimerCallback is called in a seperate thread taken from the ThreadPool). To complicate matters, the MainThread class hooks on to the Changed event of the FileSystemWatcher; when the test.xml file changes, the WorkerThread classes are recreated.</p> <p>Each time the file is saved, (each time that the WorkerThread and therefore the Timer is recreated), the memory in the Task Manager increases (Mem Usage, and sometimes also VM Size); furthermore, in .Net Memory Profiler (v3.1), the Undisposed Instances of the WorkerThread class increases by two (this may be a red herring though, because I've read that .Net Memory Profiler had a bug whereby it struggled to detect disposed classes. </p> <p>Anyway, here's the code - does anyone know what's wrong?</p> <p><strong>EDIT</strong>: I've moved the class creation out of the FileSystemWatcher.Changed event handler, meaning that the WorkerThread classes are always being created in the same thread. I've added some protection to the static variables. I've also provided threading information to show more clearly what's going on, and have been interchanging using the Timer with using an explicit Thread; however, the memory is still leaking! The Mem Usage increases slowly all the time (is this simply due to extra text in the console window?), and the VM Size increases when I change the file. Here is the latest version of the code:</p> <p><strong>EDIT</strong> This appears to be primarily a problem with the console using up memory, as you write to it. There is still a problem with explicitly written Threads increasing the memory usage. See <a href="https://stackoverflow.com/questions/483295/memory-leak-while-using-threads/553057#553057">my answer below</a>.</p> <pre><code>class Program { private static List&lt;WorkerThread&gt; threads = new List&lt;WorkerThread&gt;(); static void Main(string[] args) { MainThread.Start(); } } public class MainThread { private static int _eventsRaised = 0; private static int _eventsRespondedTo = 0; private static bool _reload = false; private static readonly object _reloadLock = new object(); //to do something once in handler, though //this code would go in onStart in a windows service. public static void Start() { WorkerThread thread1 = null; WorkerThread thread2 = null; Console.WriteLine("Start: thread " + Thread.CurrentThread.ManagedThreadId); //watch config FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = "../../"; watcher.Filter = "test.xml"; watcher.EnableRaisingEvents = true; //subscribe to changed event. note that this event can be raised a number of times for each save of the file. watcher.Changed += (sender, args) =&gt; FileChanged(sender, args); thread1 = new WorkerThread("foo", 10); thread2 = new WorkerThread("bar", 15); while (true) { if (_reload) { //create our two threads. Console.WriteLine("Start - reload: thread " + Thread.CurrentThread.ManagedThreadId); //wait, to enable other file changed events to pass Console.WriteLine("Start - waiting: thread " + Thread.CurrentThread.ManagedThreadId); thread1.Dispose(); thread2.Dispose(); Thread.Sleep(3000); //each thread lasts 0.5 seconds, so 3 seconds should be plenty to wait for the //LoadData function to complete. Monitor.Enter(_reloadLock); thread1 = new WorkerThread("foo", 10); thread2 = new WorkerThread("bar", 15); _reload = false; Monitor.Exit(_reloadLock); } } } //this event handler is called in a separate thread to Start() static void FileChanged(object source, FileSystemEventArgs e) { Monitor.Enter(_reloadLock); _eventsRaised += 1; //if it was more than a second since the last event (ie, it's a new save), then wait for 3 seconds (to avoid //multiple events for the same file save) before processing if (!_reload) { Console.WriteLine("FileChanged: thread " + Thread.CurrentThread.ManagedThreadId); _eventsRespondedTo += 1; Console.WriteLine("FileChanged. Handled event {0} of {1}.", _eventsRespondedTo, _eventsRaised); //tell main thread to restart threads _reload = true; } Monitor.Exit(_reloadLock); } } public class WorkerThread : IDisposable { private System.Threading.Timer timer; //the timer exists in its own separate thread pool thread. private string _name = string.Empty; private int _interval = 0; //thread wait interval in ms. private Thread _thread = null; private ThreadStart _job = null; public WorkerThread(string name, int interval) { Console.WriteLine("WorkerThread: thread " + Thread.CurrentThread.ManagedThreadId); _name = name; _interval = interval * 1000; _job = new ThreadStart(LoadData); _thread = new Thread(_job); _thread.Start(); //timer = new Timer(Tick, null, 1000, interval * 1000); } //this delegate instance does NOT run in the same thread as the thread that created the timer. It runs in its own //thread, taken from the ThreadPool. Hence, no need to create a new thread for the LoadData method. private void Tick(object state) { //LoadData(); } //Loads the data. Called from separate thread. Lasts 0.5 seconds. // //private void LoadData(object state) private void LoadData() { while (true) { for (int i = 0; i &lt; 10; i++) { Console.WriteLine(string.Format("Worker thread {0} ({2}): {1}", _name, i, Thread.CurrentThread.ManagedThreadId)); Thread.Sleep(50); } Thread.Sleep(_interval); } } public void Stop() { Console.WriteLine("Stop: thread " + Thread.CurrentThread.ManagedThreadId); //timer.Dispose(); _thread.Abort(); } #region IDisposable Members public void Dispose() { Console.WriteLine("Dispose: thread " + Thread.CurrentThread.ManagedThreadId); //timer.Dispose(); _thread.Abort(); } #endregion } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload