As the title states: are there any downsides to recycling resources (like large arrays) in the finalizer of the containing object? So far it works out fine but since finalizers can be a bit funky and difficult to debug I decided to ask here.
The use case is a polygon class which just represents a list of points. My application makes heavy use of large polygons with a few thousand points - allocating those arrays often isn't exactly cheap. A dispose pattern is unfortunately out of the question because they get passed around and pretty much only the GC knows when no other object is referencing them.
This is how I implemented it (arrays always have the length of 2^sizeLog2
and only arrays with sizeLog2 >= MIN_SIZE_LOG_2
get recycled):
Constructor:
public Polygon(int capacity = 1)
{
//fast method for getting the ceiling of the logarithm of an integer
int sizeLog2 = UIM.CeilingLog2((uint)capacity);
//supress finalize when the array should not be recycled
if(sizeLog2 < TWO_POW_MIN_SIZE) GC.SuppressFinalize(this);
Points = Create(sizeLog2);
}
Create array of size 2^sizeLog2:
private static Pnt[] Create(int sizeLog2)
{
if (sizeLog2 >= TWO_POW_MIN_SIZE)
{
if (/*try to get an item from recycle queue*/) return result;
Pnt[] points = new Pnt[1 << sizeLog2];
//keep array alive so that it won't get collected by GC when the polygon is
GC.KeepAlive(points);
return points;
}
return new Pnt[1 << sizeLog2];
}
In the increase capacity method this is the line for reregistering the polygon for finalize, if the finalize was suppressed up to this point (capacity can only get increased and only by a factor 2):
if (newSizeLog2 == MIN_SIZE_LOG_2) GC.ReRegisterForFinalize(this);
And this is the destructor:
~Polygon()
{
if (Points != null) Recycle(Points); //enqueues the array in a ConcurrentQueue
Points = null;
}
And yes, I do know that this isn't exactly a fancy programming style, but this is rather performance critical so I really can't just let the GC do all the work because then I end up with hundreds of MB on the large object heap in a few seconds.
Also, the polygons get handled by different threads concurrently.