假设您有一个.net对象bufferObject
,它在内部使用某种相当大的非托管内存缓冲区。它的实现方式是在处理(或完成)时释放其分配的内存。
然后,您创建一个不同的对象reusingBufferObject
,并为其提供指向bufferObject
的内部缓冲区的指针。所以这两个对象本身并不了解彼此的存在。
如果bufferObject
超出范围并被处置/最终确定,则内部缓冲区将被释放,reusingBufferObject
将访问无效内存。
当然,通过更改两个对象的底层分类的设计并让reusingBufferObject
引用bufferObject
,可以避免整个问题。但是,我们假设这两个对象都是第三方类的实例,并且它们的设计无法更改。
一个示例案例是使用一些图像处理库。必须将内部图像对象转换为Bitmap
对象以在屏幕上显示它,但出于性能原因,图像对象内部缓冲区必须由Bitmap
对象重用,而不是仅仅被复制到它
是否可以将bufferObject
的生命周期与reusingBufferObject
的生命周期联系起来,以便只有在reusingBufferObject
之后它才有资格进行垃圾回收,而无需手动跟踪生命周期?
修改
这里有一个简单的例子来说明问题:
namespace Example
{
// Some third party class with no control over its design.
// The Data property is the pointer to an unmanaged memory
// buffer.
public class ThirdPartyImage : IDisposable
{
public int Width { get; }
public int Height { get; }
public int Stride { get; }
public PixelFormat Format { get; }
public IntPtr Data { get; }
public ThirdPartyImage(string filename);
public Dispose();
}
public static class MyOwnExtensions
{
// Some method to reuse the internal memory buffer.
public static Bitmap ToBitmap(this ThirdPartyImage image)
{
return new Bitmap(image.Width, image.Height, image.Stride, image.Format,
image.Data);
}
}
class Program
{
static void Main(string[] args)
{
Bitmap bitmap = Foo();
// ThirdPartyImage is not referenced anymore and the internal buffer (Data)
// will be freed when garbage collected.
Bar(bitmap);
}
private static Bitmap Foo()
{
ThirdPartyImage image = ThirdPartyImage("foo.bmp");
Bitmap bitmap = image.ToBitmap();
return bitmap;
}
private static void Bar(Bitmap bitmap)
{
// Do some Bitmap related work here.
// The buffer could already be freed though.
}
}
}
修改
我正在寻找的东西就像垃圾收集方法,它可以让你将对象的生命周期与另一个生命周期联系起来。
或者在收集reusingBufferObject
/ Bitmap
或者有资格收集时收到某种通知,这样我就可以构建一些涉及帮助对象的自动机制,该辅助对象拥有对{{的强引用1}}和bufferObject
到WeakReference
。
手动跟踪两个实际上不再需要的对象(除了内部使用的缓冲区),例如通过引入一个包装类,对我来说似乎过于复杂。
答案 0 :(得分:1)
创建对象的人负责处理它们。如果您有objectA
和objectA
需要objectB
,但这两个对象都是由第三个对象objectC
创建的,那么objectC
需要确保同时处理这两个对象。
这是最重要的要点:创作者负责处置。
这是一些IoC容器的最大问题之一:它们创造了一些东西,但是当涉及到处理时,事情留给了其他人,这使事情变得非常棘手。
答案 1 :(得分:1)
好吧,似乎ConditionalWeakTable<TKey, TValue>正在做我想做的事。 通过稍微改变一下这个例子,它应该可以工作:
public static class MyOwnExtensions
{
private static readonly ConditionalWeakTable<Bitmap, ThirdPartyImage>
WeakBitmapImageTable = new ConditionalWeakTable<Bitmap, ThirdPartyImage>();
public static Bitmap ToBitmap(this ThirdPartyImage image)
{
Bitmap bitmap = new Bitmap(image.Width, image.Height, image.Stride, image.Format,
image.Data);
WeakBitmapImageTable.Add(bitmap, image);
return bitmap;
}
}
答案 2 :(得分:0)
您问题的第一部分非常通用,无法按原样回答。我的回答只是尝试回答问题中的特定代码示例。
由于ThirdPartyImage
实现了IDisposable
,因此它的预期行为是它保留该缓冲区,直到您在其上调用Dispose()
。如果您不想复制图像缓冲区,则必须在位图的整个生命周期内保留对ThirdPartyImage
的引用,否则当缓冲区释放时,程序将在某个时刻崩溃并且位图无法呈现。
您可以通过多种方式执行此操作 - 使用ThirdPartyImage
实例的全局映射,由位图键入(这很棘手且可能很快成为维护问题),或者您可以保留两个对象({{1}你传递给每个地方的一个类中的位图(你可以)。这样,当您准备摆脱位图时,您只需销毁持有者对象,该对象将在位图上调用ThirdPartyImage
,然后在Dispose()
上调用。
你可以试着想出一些方法来隐藏这种类似的设置,这些设置似乎可以自动化,但最后一个对象到达另一个对象并将它们联系在一起。只有你知道何时摆脱第一个物体是安全的,这样第二个物体也可以消失。