Memory leak in C # when using C ++ / CLI class with finalizer

When I implement a class in the C ++ / CLI DLL:

public ref class DummyClass { protected: !DummyClass() { // some dummy code: std::cout << "hello" << std::endl; } } 

and when I load this DLL into a C # project and use the class simply by repeatedly creating the object:

 static void Main() { while (true) { var obj = new DummyClass(); } } 

then when the program starts, the memory is slowly digested into an OutOfMemoryException.

It seems that this memory leak (or the bad garbage collection work) happens every time I implement a finalizer in C ++ / CLI.

Why is there a memory leak? How can I avoid this and can still use the finalizer for another (more complex) use?


UPDATE: The reason, of course, is not in writing to Console / stdout or other custom code in the finalizer, the following class has the same memory leak behavior:

 public ref class DummyClass { private: double * ptr; public: DummyClass() { ptr = new double[5]; } protected: !DummyClass() { delete [] ptr; } } 
+4
source share
2 answers

When distributing faster than you can garbage collect, you run in OOM. If you make large selections, the CLR will insert sleep (xx) into the throttle distribution, but this is not enough in your last resort.

When you implement a finalizer, your object is added to the finalization queue, and when it has been finalized, it is removed from the queue. This imposes additional overhead and you will make your object life longer than necessary. Even if your object can be freed during the cheap Gen 0 GC, it still refers to the completion queue. When a full GC occurs, the CLR starts the finalization stream to begin the cleanup. This does not help, since you are allocating faster than you can complete (writing to stdout is very slow), and your finalization queue will become larger and larger, which will lead to a slower and slower completion time.

I did not evaluate it, but I think that even an empty finalizer will cause this problem, since the increased lifetime of the object and two queues of the queue until completion (finalizer queue and f-achievable queue) impose enough overhead to make finalization slower than distribution .

You must remember that finalization is an integral asynchronous operation without performance guarantees at a particular point in time. The CLR will never wait for all pending finalizers to clear before allowing additional allocations. If you allocate 10 threads, the finalizer stream will still clear after you. If you want to rely on deterministic finalization, you will need to wait by calling GC.WaitForPendingFinalizers () but this will stop your work.

Therefore, your OOM is expected.

+3
source

You must use the AddMemoryPressure function, otherwise the garbage collector underestimates the need for timely cleaning of these objects.

0
source

All Articles