When you compile .NET code for the Release target, the garbage collector is really aggressive, meaning it has potential.
Take this example:
public void Test() { FileStream stream = new FileStream(....); stream.Write(...); SomeOtherMethod(); }
In this example, after calling Stream.Write this method is no longer used for the stream variable and therefore is considered out of scope, which means that the FileStream object now has a collection right. If the garbage collector is running while calling SomeOtherMethod , a stream object can be collected.
Change As @Greg Beech notes in the comments, an object can be assembled even if it is currently executing an instance method.
For example, let's say the code in FileStream.Write looks like this:
public void Write(byte[] buffer, ...) { IntPtr unmanagedHandle = _InternalHandleField; SomeWindowsDll.WriteBuffer(unmanagedHandle, ...); ...
here, the method first captures an internal field containing an unmanaged descriptor. The object will not be assembled before this happens. But if this is the last field of the object or the last instance-method of the object that is accessed in Write, before switching to just P / Invoke calls, this object now has the right to collect.
And since FileStream probably has an unmanaged file descriptor (maybe I don’t know), it probably also has a finalizer, so an unmanaged file descriptor can be closed before the WriteBuffer call completes, That is, if the intended .Write implementation is as indicated above, which is most likely not.
If you want to prevent this, make the thread object active for the duration of the SomeOtherMethod call for some reason, you need to insert the code after the call, which somehow "uses" the object. If you do not want to call a method or read an object property, you can use the GC.KeepAlive artificial “use” method for this:
public void Test() { FileStream stream = new FileStream(....); stream.Write(...); SomeOtherMethod(); GC.KeepAlive(stream); }
The method does nothing, but it has been marked with attributes, so it will not be optimized. This means that the stream variable is now used for the duration of the SomeOtherMethod call, and the FileStream object stored in it will not be collected during this time.
That is all he does. GC.KeepAlive does not leave a permanent label on anything, it does not change the lifetime of the object after it is called, it simply adds code that “uses” the variable at some point, which prevents the garbage collector from collecting the object.
I found out about this method, or rather, about the point of this method, about a complex method with some code that looked like this:
public void Test() { SomeObjectThatCanBeDisposed d = new SomeObjectThatCanBeDisposed(); SomeInternalObjectThatWillBeDisposed i = d.InternalObject(); CallSomeMethod(i); }
Please note that I am creating an instance of an object that implements IDisposable , and in the original case, it also implemented a finalizer. Then I “fished out” the object that he was tracking, and the way to create a one-time object (wrong) was that after starting the finalizer, it would also delete the internal objects (which is bad). In the above code, d becomes available for collection after the internal object has been extracted, and as soon as the object is collected, it completes by deleting the object that I extracted. This means that during the call to CallSomeMethod object passed to the method died, and I could not understand why.
Of course, the fix for the above code was to not use GC.KeepAlive , but instead fix the incorrect finalizer, but if the "internal object" was an unmanaged resource, it could be otherwise.
Now, for your keyboard, if you store the link in the root like a static field, a member field of an object, which is also stored in memory, etc., then it should not be collected from the very beginning.