Is there something wrong with GC.KeepAlive spam (KeyboardHookPointer)?

GC.KeepAlive ()

References to the specified object, which makes it unacceptable for garbage collection from the beginning of the current routine to the point at which this method is called.

Not quite sure what GC.KeepAlive does, other than just storing the link so that the garbage collector does not collect the object. But does calling GC.KeepAlive () on an object constantly keep the object from being collected? Or do you need to call GC.KeepAlive () repeatedly so often (and if so, how often)? I want my keyboard to be alive.

+4
source share
2 answers

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.

+10
source

GC.KeepAlive (as well as GC.Collect) should not be used in production code, since it does not solve any problems, it simply creates more race conditions. They are only useful for diagnosing a problem.

If you have a managed object that is used by the native code, so the garbage collector cannot see that it is still in use, you should use GCHandle.Alloc .

EDIT: some supporting evidence:

GC.KeepAlive is not very good, and it is definitely useful only when using your own code is guaranteed to end before returning, which does not apply to the keyboard hook.

+2
source

Source: https://habr.com/ru/post/1306636/


All Articles