7

Or is it already to late if the finalize method is reached?

Basically I'm creating some code to log to a MySql database. Each log entry is represented by an object and stored in a queue until it gets flushed to the database in a batch insert / update. I figured it'd be inefficient to create a new object on the heap every time I wanted to write an entry (especially since I might want to write an entry or two in performance sensitive areas). My solution was to create a pool of objects and reuse them.

Basically I'm trying to not re-invent the wheel by letting the .Net Garbage Collector let me know when an object is no longer needed and can be added back to the pool. The problem is I need away to abort garbage collection from the destructor. Is that possible?

ForeverNoobie
  • 531
  • 5
  • 17
  • 1
    Yes you can . Take a loot at: http://stackoverflow.com/questions/3680281/usages-of-object-resurrection – DarthVader Mar 03 '14 at 19:21
  • Care to elaborate? Why not just reuse the objects and never even unreference them? I do not see why you need to break the garbage collector. – flindeberg Mar 03 '14 at 19:21
  • What do you mean by "abort garbage collection"? Do you mean that you don't want the objects finalizer called by the GC? – Gabe Mar 03 '14 at 19:24
  • Why not just split the pool into two groups. Unused objects, and currently being used objects. Give each object event to tell the pool when it's been saved. That controls which group it goes into. – Reactgular Mar 03 '14 at 19:24
  • @MathewFoscarini that's what I did at first, but unfortunately I had to be an over achiever and I introduced a feature that broke that approach. This is a heavily multithreaded app (which is why I'm logging via MySql) and so I wanted a way to have log entries logically arrangable for easy searching. I made a feature where every log entry has a parent entry and every parent entry keeps track of how many children it has. This means even after a log entry has been flushed to the database it's not available for reuse until all it's children have been. I figured Hi-jacking GC would be easiest way – ForeverNoobie Mar 03 '14 at 19:33
  • @user1379635 how about this http://msdn.microsoft.com/en-us/library/ff458671(v=vs.110).aspx – Reactgular Mar 03 '14 at 20:10

3 Answers3

4

Can you? Yes.
Should you? No, it is almost certainly a terrible idea.

The general rule C# developers should remember is the following:

If you find yourself writing a finalizer, you probably did something wrong.

The memory allocators used by well-established managed VMs (such as the CLR or JVM) are extremely fast. One of the things that slows down the garbage collector in these systems is the use of customized finalizers. In an effort to optimize the runtime, you are actually giving up a very fast operation in favor of a much slower operation. Furthermore, the semantics of "bringing an object back to life" are difficult to understand and reason about.

Before you consider using a finalizer, you should understand everything in the following articles.

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
3

Connection pooling is a feature virtually any major DB connection implementation is already going to natively support, so there is no reason to handle this manually. You'll be able to simply create a new connection for each operation and know that behind the scenes the connections will actually be pooled.

To answer the literal question that you asked, yes. You can ensure that an object is not going to be GCed after it is finalized. You can do so simply by creating a reference to it from some "live" location.

This is a really bad idea though. Take a look at this example:

public class Foo
{
    public string Data;
    public  static Foo instance = null;
    ~Foo()
    {
        Console.WriteLine("Finalized");
        instance = this;
    }
}

public static void Bar()
{
    new Foo() { Data = "Hello World" };
}

static void Main(string[] args)
{
    Bar();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine(Foo.instance.Data);
    Foo.instance = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();
}

This will print out:

Finalized

Hello World

So here we had an object end up being finalized, and we then accessed it later on. The problem however is that this object has been marked as "finalized". When it is finally hit by the GC again it's not finalized a second time.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • I don't think he's talking about db connections. But log entry objects. – Reactgular Mar 03 '14 at 19:23
  • 1
    @MathewFoscarini `"I'm creating some code to log to a MySql database"`. What he's really trying to pool here is a DB connection. Even if he's technically trying to pool the log object, clearly the "expensive" part of that log object is the DB connection; that's what he's trying to re-use. – Servy Mar 03 '14 at 19:24
  • Well, the MySql connector SDK explicitly says not to cache db connections as the connector already does that for you. – Reactgular Mar 03 '14 at 19:26
  • No. The expense I'm trying to avoid here is allocating log objects on the heap. It's splitting hairs in most cases but I'm making a realtime game server so hairs shall be split. – ForeverNoobie Mar 03 '14 at 19:27
  • 1
    @user1379635 In that case you're almost certainly better off not doing this. Unless the pooled objects are very time consuming to create/dispose of, you're almost always going to be *harming* performance trying to pool objects yourself. It takes very little time to allocate objects in memory, and they add very little overhead to the GC if they are short lived. In virtually any implementation the overhead of pooling items is higher, if there is no additional expensive initialization of objects. – Servy Mar 03 '14 at 19:30
  • @user1379635 Are you sure that ALLOCATION is the problem? Have you [run your horses](http://ericlippert.com/2012/12/17/performance-rant/)? – flindeberg Mar 03 '14 at 19:31
  • @Servy do you mean the memory overhead for pooling will cancel out the processing gains? or will polling be more expensive on the processor? Because I'm coding it carefully to be efficient. I couldn't imagine moving an object on and off a queue would be harder on the processor than allocating in the long run. Admittedly I haven't done much performance testing. – ForeverNoobie Mar 03 '14 at 19:38
  • @Servy I take those are typos in the first sentence: polling->pooling, naively->natively, and not->no. The first two somewhat change the meaning away from my understanding of how it works. – Andrew Morton Mar 03 '14 at 19:40
  • @user1379635 You're overestimating just how easy it is for the GC to allocate a new object. It's simply not an expensive operation. Getting the next item from a queue can actually be more expensive. Then of course there are things that you need to consider such as is your pool going to dynamically expand/contract based on use? If so, those checks to see if you should/shouldn't, as well as the overhead of actually performing those changes, can *easily* be more significant than what the GC is doing. – Servy Mar 03 '14 at 19:41
  • @AndrewMorton Yes, those were typos. – Servy Mar 03 '14 at 19:41
  • It's not safe to call `Console.WriteLine` in a finalizer. – Sam Harwell Mar 03 '14 at 20:10
  • @280Z28 It's not a production application. It's merely a program to demonstrate a concept. In an *actual* implementation there would be no need to access the console. I also specifically mention that it shouldn't be used, despite the fact that it's *possible*. – Servy Mar 03 '14 at 20:13
  • @Servy I decided to not go with this approach. To be honest I've spent lots of time shooting myself in the foot with things like this before. The more I think about it the more I agree that this is unwarranted. Glad I asked before spending to much time. – ForeverNoobie Mar 04 '14 at 16:24
0

You could re-register for finalization in the destructor, like so:

~YourClass()
{
   System.GC.ReRegisterForFinalize(this);
}

And from there you'd probably want something to reference so it doesn't get finalized again, but this is a way to do it.

http://msdn.microsoft.com/en-us/library/system.gc.reregisterforfinalize(v=vs.110).aspx

T McKeown
  • 12,971
  • 1
  • 25
  • 32
  • 1
    This doesn't work. Documentation states: "Requests that the system call the finalizer for the specified object for which SuppressFinalize has previously been called". The finalizer would never be called if SuppressFinalize() was first called. Even the example at msdn doesn't work. – HelloWorld Jun 25 '17 at 12:00