Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Releasing memory usage

Tags:

I need to execute a long-heavy process for a question out of the box. So I have divided the process in multiple subprocess. The question now, is how to release memory, before the execution of each window.

It's easier to explain with an example. Let's take a look to this pseudo-code.

1. Some earlier code to do other things
2. Do
3.     Raise a Task
4.     If raised-task > 1000
5.         Wait all raised task to finish
6.         Release Memory
7.     End If
8. Wile Something not relevant

With that idea, I have developed the next method, which gets executed every time it's reached the thread limitation:

List<Task> LTask();
//It's not relevant, but this list is populate like
//var task = Task.Run(() => something());
//LTask.Add(task);

private void waitForAll()
{
    //Break point 1
    Task.WhenAll(LTasks).Wait();

    LTasks.Clear();
    LTasks = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();

    //Break point 2
    LTasks = new List<Task>();
}

I expected memory gets constant (whit some variation) around some values. I mean:

  1. Thread limit is reached
  2. Snapshot of memory usage with visual studio diagnosis tools in BreakPoint 1 --> 100MB
  3. Snapshot of memory usage with visual studio diagnosis tools in BreakPont 2 --> 100 MB. First question, why this has not decreased? All the threads are finished and I forced Garbage Collector to execute.

Next time the limit is reached and this code is executed, if I take a snapshot again, the memory keeps increasing: 200, 300, ...

This is a capture of diagnosis tools. Odd snapshots are taken every time break point 1 is reached, and Even snapshots on break point 2.

enter image description here

Second question, this will continue increasing whit no limit until it throws an Out of memory Exception?

Last question, any alternatives to solve the problem, and release the memory?

UPDATE 1: After some tests, and thanks to the comments, I have developed a test code, to dig into it. There has to be involved something else. Plase, take a look to the next piece of code. The memory continue increasing with no limit.

private List<Task> LTasks = new List<Task>();
private void manageThreadholdLimit()
{
    waitForAll();
    realeaseMemory();
}

private void waitForAll()
{
    Task.WhenAll(LTasks).Wait(); 
    LTasks.Clear();
    LTasks = null;  
}
private void realeaseMemory()
{   
    GC.Collect();
    GC.WaitForPendingFinalizers();

    LTasks = new List<Task>();
}
public void Main(){
    int i = 0;

    while (true)
    {
        i++;

        var task = Task.Run(() => Thread.Sleep(100));
        LTasks.Add(task);

        //Si hemos alcanzado el máximo de paralelismo, esperamos la conclusión
        if (i % 1000 == 0) manageThreadholdLimit();

    }
}
like image 972
Rumpelstinsk Avatar asked Nov 19 '19 13:11

Rumpelstinsk


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C in C?

Full form of C is “COMPILE”. One thing which was missing in C language was further added to C++ that is 'the concept of CLASSES'. So ++ being the increment operator, C has an incremented version called as “C++”.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Why is C language popular?

It is fast The programs that you write in C compile and execute much faster than those written in other languages. This is because it does not have garbage collection and other such additional processing overheads. Hence, the language is faster as compared to most other programming languages.


2 Answers

GC collection is slightly different in debug, see:(John Skeet knows all) So I would do allot of logging when running this in release mode to verify all behaviours.

Any solution will be very dependent on your actual code and if there are unmanaged resources being accessed.

That said I have had to deal with a problem like this before and I have "solved" it in 2 different ways before.

One solution

Have counters that are incremented in constructor and decremented in the finalizer in the class that does the actual work and wait for the counter to fall under a defined threshold and importantly run collect again for the finalized objects to be collected.

Be careful checking the total memory consumption before continuing otherwise you can end up with out of memory exception.

Now this will actually increase your memory consumption slightly. For more info see

Another solution

Have a loop waiting for the memory consumption to fall by using GC.GetTotalMemory() or even better performance counters and wait for it to come down.

This can end up not doing any work at all if your resources are not being collected.

like image 98
Archlight Avatar answered Oct 06 '22 00:10

Archlight


It is not guaranteed that the GarbageCollector will run immediately after you unreference the object. In fact, it probably won't. Even if you want to call it manually for testing purposes with GC.Collect() you have no real guarantees it will run immediately. Also, there is a cost to calling GC often. RAM is there to be used (at least in newer machines with lots of RAM...). You have a problem if your memory goes up, and stays up after a longer while. This usually indicates that you have some other memory problem. Maybe you have a leak?

If we're talking about free solutions, you can use ProcessExplorer and CLR Profiler to look for potential memory problems. Here is an article on how to do that.

Things to look out for:

  1. Set references to your objects to null as soon as you can. You are done with the list? Set its value to null.
  2. Split the method that is working with a lot of data into smaller methods - even if you set something to null, the GC won't run until a method is exited.
  3. Check if you have finalizers properly implemented where needed.
  4. Make sure you do not have leaks: check if the objects you are working with aren't referenced from outside the worker method. Be especially careful about event handlers and data bindings.

Also, Task.WhenAll() apparently keeps the references to all its "child" tasks. If you manually call GC immediately after it in the same method, I think there is a chance it won't be able to touch that memory, as the method itself is still "referencing" it.

Here is a MSDN article about garbage collection.

like image 40
Arie Avatar answered Oct 06 '22 00:10

Arie