Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of event memory leak in C #

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Editor to share with you the example analysis of event memory leak in C#, I believe that most people do not know much about it, so share this article for your reference. I hope you will learn a lot after reading this article. Let's learn about it together.

Memory leak example

To demonstrate how memory leaks occur, let's look at a piece of code

Class Program {static event Action TestEvent; static void Main (string [] args) {var memory = new TestAction (); TestEvent + = memory.Run; OnTestEvent (); memory = null; / / mandatory garbage collection GC.Collect (GC.MaxGeneration); Console.WriteLine ("GC.Collect"); / / Test whether the collection is successful OnTestEvent (); Console.ReadLine ();} public static void OnTestEvent () {if (TestEvent! = null) TestEvent () Else Console.WriteLine ("Test Event is null");} class TestAction {public void Run () {Console.WriteLine ("TestAction Run.");}

In this example, memory.run subscribes to the TestEvent event, and after raising the event, you will see TestAction Run on the screen. When memory = null, there is no instance of the block of memory that memory originally points to, and that memory is the memory to be reclaimed. The GC.Collect (GC.MaxGeneration) statement forces a garbage collection, raises the event again, and finds that TestAction Run is still displayed on the screen. This memory is not reclaimed by GC, which is an inner pure leak. This is caused by the TestEvent+=memory.Run statement, and when GC.Collect executes, when he sees that the block of memory has a TestEvent reference, it will not be reclaimed. But the memory is already "unreachable", that is, the block of memory cannot be called, and the Run method of the memory can only be executed when an event is raised. This is obviously not what I want. When memory = null is executed, I want the memory to be recycled when the GC is executed, and when the TestEvent is thrown, the Run method will not execute, because I have "freed" the memory.

There is a problem here, that is, how to "free" a piece of memory in C#. In languages like C and C++, developers are responsible for the declaration and release of memory. Once the memory new is out, it is necessary to delete, otherwise it will cause a memory leak. This is more flexible and troublesome, accidentally leaking, forgetting to release, thread exception without executing the released code. A language that allocates memory manually has a language that is automatically allocated and freed. The first language to use garbage collection was LISP, and then it was used in managed languages such as Java and C #. For example, when CLR detects that garbage memory is worthy of a garbage collection after the program has been executed for a period of time, the CLR performs garbage collection. As for how to determine whether a piece of memory is garbage memory, the more famous is the counting method, that is, after an instance references the memory, it is + 1 on the memory count, and the instance cancels the reference to the memory, the count is-1. When the count is 0, it is judged to be garbage. The problem with this approach is that there is nothing you can do about circular references, such as a field in A refers to B and a field in B refers to A, so that neither A nor B's technology can be reduced to 0. The method CLR uses instead is similar to "tag reference" (my own name): when GC is executed, all threads are suspended, all memory in the managed heap is marked with junk, and then all reachable instances are traversed, which, if they reference the memory of the managed heap, change the marking of that memory from garbage to being referenced. When An and B refer to each other, if there are no other instances to reference An or B, although An and B refer to each other, both An and B are unreachable, that is, there is no way to quote An or B. then both An and B will be judged as garbage and recycled. The purpose of explaining this pile is to say that in C #, if you want to free a piece of memory, you just have to let that block of memory have no instance to reference it. So when memory = null is executed, no instance references that block of memory except for a subscription to TestEvent, so why does the subscription event prevent the release of memory?

Let's see what the sentence TestEvent+=memory.Run () does. We decompiled the above dll using IL, and we can see

IL_0000: nopIL_0001: newobj instance void EventLeakMemory.Program/TestAction::.ctor () IL_0006: stloc.0IL_0007: ldloc.0IL_0008: ldftn instance void EventLeakMemory.Program/TestAction::Run () IL_000e: newobj instance void [mscorlib] System.Action::.ctor (object, native int) IL_0013: call void EventLeakMemory.Program::add_TestEvent (class [mscorlib] System.Action). / / other parts

The key lies in lines 5-7. Lines 5 and 6 declare a system. Action type delegate with the parameter TestAction.Run method, and line 7 executes the Program.add_TestEvent method, and the parameter is the delegate declared above. In other words, the + = operator is equivalent to executing Add_TestEvent (new Action (memory.Run)), which is the new Action that contains a reference to the memory that memory points to. This reference is reachable to CLR and can be invoked by raising an event.

Solution.

We have found the culprit of the memory leak, which is the reference to memory by the anonymous delegate implicitly declared when subscribing to the event. The solution to this problem is to refer to the instance object of the method in a different way than a normal reference: the reference does not affect garbage collection and is not determined to be a reference to that memory at GC, that is, a "weak reference". In C #, most types are strong references. How to implement weak references? Let's look at an example:

Static void Main (string [] args) {var obj = new object (); var gcHandle = GCHandle.Alloc (obj, GCHandleType.Weak); Console.WriteLine ("gcHandle.Target = = null is: {0}", gcHandle.Target = = null); obj = null; GC.Collect (); Console.WriteLine ("GC.Collect"); Console.WriteLine ("gcHandle.Target = = null is: {0}", gcHandle.Target = null); Console.ReadLine ();}

When performing GC. After Collect, gcHandle.Target = = null is changed from false to true. This gcHandle is a weak reference to obj. A detailed description of this class can be found in GCHandle. The key is the second parameter of the GCHandle.Alloc method, which accepts an enumerated type. I'm using GCHandleType.Weak, indicating that the reference is a weak reference. Using this method, you can encapsulate your own WeakReference class with the following code

Public class WeakReference where T: class {private GCHandle handle; public WeakReference (T obj) {if (obj = = null) return; handle = GCHandle.Alloc (obj, GCHandleType.Weak); whether the referenced target is still alive (not reclaimed by GC) / public bool IsAlive {get {if (handle = = default (GCHandle)) return false; return handle.Target! = null }} / public T Target of the reference {get {if (handle = = default (GCHandle)) return null; return (T) handle.Target;}

With this class, you can write your own weak event wrapper.

Public class WeakEventManager {private Dictionary delegateDictionary; public WeakEventManager () {delegateDictionary = new Dictionary ();} / subscribe / public void AddHandler (Delegate handler) {if (handler! = null) delegateDictionary [handler] = new WeakReference (handler);} / unsubscribe / public void RemoveHandler (Delegate handler) {if (handler! = null) delegateDictionary.Remove (handler) } / public void Raise (object sender, EventArgs e) {foreach (var key in delegateDictionary.Keys) {if (delegateDictionary [key] .IsAlive) key.DynamicInvoke (sender, e); else delegateDictionary.Remove (key);}

Finally, you can define your own events as follows

Public class TestEventClass {private WeakEventManager _ testEvent = new WeakEventManager (); public event Action TestEvent {add {_ testEvent.AddHandler (value);} remove {_ testEvent.RemoveHandler (value);}} protected virtual void OnEvent (EventArgs e) {_ testEvent.Raise (this, e);}} these are all the contents of the article "sample Analysis of event memory leaks in C#". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report