How to make the JIT extend stack variables to the end of scope (GC is too quick)
We're dealing with the GC being too quick in a .Net program. Because we use a class with native resources and we do not call GC.KeepAlive(), the GC collects the object before the Native access ends. As a result the program crashes. We have exactly the problem as described here:
Does the .NET garbage collector perform predictive analysis of code?
Like so:
{ var img = new ImageWithNativePtr();
IntPtr p = img.GetData();
// DANGER!
ProcessData(p);
}
The point is: The JIT generates information that shows the GC that img is not used at the point when GetData() runs. If a GC-Thread comes at the right time, it collects img and the program crashes. One can solve this by appending GC.KeepAlive(img); Unfortunately there is already too much code written (at too many places) to rectify the issue easily.
Therefore: Is there for example an Attribute (i.e. for ImageWithNativePtr) to make the JIT behave like in a Debug build? In a Debug build,开发者_Python百科 the variable img will remain valid until the end of the scope ( } ), while in Release it looses validity at the comment DANGER.
To the best of my knowledge there is no way to control jitter's behavior based on what types a method references. You might be able to attribute the method itself, but that won't fill your order. This being so, you should bite the bullet and rewrite the code. GC.KeepAlive
is one option. Another is to make GetData
return a safe handle which will contain a reference to the object, and have ProcessData
accept the handle instead of IntPtr
— that's good practice anyway. GC will then keep the safe handle around until the method returns. If most of your code has var
instead of IntPtr
as in your code fragment, you might even get away without modifying each method.
You have a few options.
- (Requires work, more correct) - Implement
IDisposable
on yourImageWithNativePtr
class as it compiles down totry { ... } finally { object.Dispose() }
, which will keep the object alive provided you update your code withusing
s. You can ease the pain of doing this by installing something like CodeRush (even the free Xpress supports this) - which supports creatingusing
blocks. - (Easier, not correct, more complicated build) - Use Post Sharp or Mono.Cecil and create your own attribute. Typically this attribute would cause GC.KeepAlive() to be inserted into these methods.
The CLR has nothing built in for this functionality.
I believe you can emulate what you want with a container that implements IDispose, and a using statement. The using statement allows for defining the scope and you can place anything you want in it that needs to be alive over that scope. This can be a helpful mechanism if you have no control over the implementation of ImageWithNativePtr.
The container of things to dispose is a useful idiom. Particularly when you really should be disposing of something ... which is probably the case with an image.
using(var keepAliveContainer = new KeepAliveContainer())
{
var img = new ImageWithNativePtr();
keepAliveContainer.Add(img);
IntPtr p = img.GetData();
ProcessData(p);
// anything added to the container is still referenced here.
}
精彩评论