0

Edit:I am trying to use Constrained Execution Regions as a new weapon against Abort(). I am still running test, I hope it work. Abort() is really a bad thing. I will report later.. If anyone has any argument against CER please note that.

I am having a problem about .NET memory leaking.
I have a project, quite complex, threads in threads in threads, makes it hard to debug.

I know Abort() is depreciated, but I have my reasons to use them:

  1. My method is a long method, I/O, network related. There's no explicit time-consuming operation that I can put a flag inside. If I put flags everywhere the code will be a mess.

  2. When needed, the thread has to be terminated at once, sooner is better. All the work inside is no longer needed.

When I run the program normally, by which threads finish their work and die naturally one by one, everything is ok (I have run my program for 2 years, no memory leaking).
But if I try to create new thread, Start() and Abort() them frequently (1-2 threads/second, just a bit faster than the case I don't abort()), the memory leaks.
And the more wired thing is that, after the operation is finished, the memory occupied will stay high for a few time, like minutes or 10 minutes, but it will finally return to normal level, like nothing happened.

In debug mode, I can see no active thread, but memory leaked.

So I used .NET memory profiler to trace. Most instances occupied the memory are many byte[] referenced by MemoryStream.
Yes, I do use MemoryStream, but ALL of my MemoryStreams are inside using blocks, no exception. Everything should be in using block correctly.
As far as I know, Thread.Abort() is throwing an exception to force it to close, but as I confirmed in debug mode, all the threads are closed expectedly. Why there are still reference? Why the memory is released after some time? (But still much longer than the situation when I don't abort the thread and let it finish the work.)
IMO using blocks can guarantee that even the ThreadAbortException is thrown inside the Dispose() can be executed correctly.

Edit:

enter image description here

enter image description here

public static byte[] Recover(byte[] png)
    {
        using (MemoryStream pngStream = new MemoryStream(png))
        {
            using (System.Drawing.Image image = System.Drawing.Image.FromStream(pngStream))
            {
                using (MemoryStream bmpStream = new MemoryStream())
                {
                    image.Save(bmpStream, System.Drawing.Imaging.ImageFormat.Bmp);
                    bmpStream.Seek(0, System.IO.SeekOrigin.Begin);
                    byte[] BMP = new byte[bmpStream.Length];
                    bmpStream.Read(BMP, 0, (int)bmpStream.Length);
                    return BMP;
                }
            }
        }
    }


public xxxxMission(byte[] png, Server server, int no) //constructor 
        {
            byte[] bmp = xxxBMP.Recover(png);  //png is generated by getBlankBMP();
            //....
        }
T.Worm
  • 382
  • 2
  • 11
  • 2
    `like minutes or 10 minutes, but it will finally return to normal level, like nothing happened` - sounds like properly working garbage collection to me. `Dispose()` [does not destroy objects](https://stackoverflow.com/q/655902/11683). – GSerg May 04 '19 at 21:40
  • This instance has been disposed but has still not been GCed. This might indicate a memory leak since disposed instances should normally no longer be used. Why GC.Collect() doesn't work? – T.Worm May 05 '19 at 11:59
  • 6
    You discovered the Hard Way that *using* statements are not safe against thread aborts. You have no guarantee that the Image.Dispose() method gets called, that's how the underlying GPStream can survive. The next GC gets rid of it. This kind of code probably requires 64-bit execution anyway, too many potentially large byte[] arrays, so be sure to turn off the "Prefer 32-bit" checkbox. https://stackoverflow.com/a/14831590/17034 – Hans Passant May 05 '19 at 12:02
  • 2
    `long method, I/O, network related`... doesn't get aborted with `Thread.Abort`. There's no aborts while running native code, which includes I/O. Half the reason `Abort` was deprecated is that it's extremely difficult to write correct code that uses thread aborts. The second half is that it's much less useful than it sounds. – Luaan May 05 '19 at 12:07
  • @Hans Passant Do you know why the memory will return to normal in this case? The time is random, maybe a few minutes but usually less than 10 minutes. And GC.Collect() seems doesn't work AT ALL. – T.Worm May 05 '19 at 14:10
  • I have to say that this may not work, for the abort can still be between assignment and constructor. But I am still trying. – T.Worm May 06 '19 at 02:29
  • @T.Worm - Please don't edit questions as much as you have. The correct way is to only edit mistakes in your question and to add new information to the bottom. Your aim is to make your question easy to understand without invalidating any existing answers. – Enigmativity May 06 '19 at 11:38
  • @Enigmativity noted. Sorry for that. but my edition never invalid any answers. I just added/removed the solution I added which I thought was right but actually wrong. – T.Worm May 06 '19 at 12:22
  • @T.Worm - If you have a solution you should add it as an answer. Solutions shouldn't go in questions. – Enigmativity May 06 '19 at 12:33
  • @Enigmativity What if I am not sure my solution is correct or not? Can I also add them? In this case my previous one was soon proved wrong(by myself). – T.Worm May 06 '19 at 12:35
  • You don't want to "put flags everywhere." But it sounds like your intent is to be able to start whatever this operation is and then cancel it if needed, so if there's any way to manage that in your code it would be better. Can you pass a cancellation token to your method? What are the operations going on inside the method? Can they be cancelled? I'd consider that route because then someone looking at that message in isolation can tell that it's meant to be possible to cancel it. – Scott Hannen May 06 '19 at 19:07

0 Answers0