0

I have a complex algorithm that works with 30+ GB of memory, and I need to optimize it because I'm getting System.OutOfMemory exception sometimes.

For example, imagine this code:

public void DoWork()
{
    HashSet<MyStruct> hashSet = LoadALotOfStructs();
    List<MyStruct> list = hashSet.ToList();

    // Lot of code that can not use the hashSet anymore
}

Now, since I will never use the HashSet again, I want to tell GC to get rid of the HashSet and free memory immediately. So, I am wondering about this simple change:

public void DoWork()
{
    List<MyStruct> list;

    {  // just this
        HashSet<MyStruct> hashSet = LoadALotOfStructs();
        list = hashSet.ToList();
    }  // and this

    // Lot of code that can not use the hashSet anymore
}

Can this be done? Does the GC clean the objects instantiated inside the { } block when they get out of context?

Also, keep in mind that this is just an example that illustrates my question, the code is very different.

Guilherme
  • 5,143
  • 5
  • 39
  • 60
  • 2
    You could just as easily do `list = LoadALotOfStructs().ToList();` – A Friend Dec 05 '18 at 16:36
  • 1
    The garbage collector does collect garbage from everywhere, but it doesn't run immediately, unless you force it to (not recommended in the general case). – 500 - Internal Server Error Dec 05 '18 at 16:37
  • In a release build, the compiler/JITter should put in enough information in your code to let the GC know that `hashSet` is eligible for collection once you finish using it (without you doing anything special). I'm assuming you have a good reason to create two copies of your data (one in the `HastSet` and the other in the `List`). As @500-InternalServerError says, the GC won't run immediately (though it will run pretty quickly if you are running out of memory). You may want to walk through your algorithm to see if you can reduce the garbage you produce. – Flydog57 Dec 05 '18 at 16:39
  • Why dows it matter the GC? Any case you can't reference to `hashSet`, since it's inside an internal block. – Chayim Friedman Dec 05 '18 at 16:40
  • @AFriend Yes, I know that, but unfortunately I cannot do that in the real algorithm. I don't know how to illustrate better my doubt without letting margin for an optimization that "solves" this problem without focusing purely on my question, and without pasting 250+ lines of code – Guilherme Dec 05 '18 at 16:41
  • Read this (and follow the links it points to): https://stackoverflow.com/questions/17497923/when-is-an-object-eligible-for-garbage-collection – Flydog57 Dec 05 '18 at 16:43
  • @500-InternalServerError No, the GC runs after: 1) You force it to run, as you said. 2) At the end of the program. 3) When there isn't more place in the memory. – Chayim Friedman Dec 05 '18 at 16:43
  • 2
    @ChayimFriedman: Uh, no. The GC runs all on it's own when it feels like it needs to run. The usual description is "when there's _memory pressure_ - whatever that means. It definitely doesn't wait until "there isn't more place in the memory" – Flydog57 Dec 05 '18 at 16:45
  • The additional `{ }` most likely do not make a difference, because the garbage collector can collect a variable [even before the current block ends](https://blogs.msdn.microsoft.com/oldnewthing/20100810-00/?p=13193). – GSerg Dec 05 '18 at 16:50
  • One thing to think about is whether any or your "objects" are "large objects". If your code allocates anything over about 85 kb in a single allocation, then the runtime uses different allocation and collection rules (your immense hash set is made up of many small allocations, so it likely doesn't count). Read up on the _Large Object Heap_ (aka LOH) to find out how the GC handles LOH cleanup. There are a bunch of good garbage prevention strategies around (#1 is don't do a lot of string concatenation). – Flydog57 Dec 05 '18 at 17:08
  • 2
    Additional braces like that are purely lexical. They do not have a runtime effect like running the garbage collector. The primary use for this is to reuse local variable names by putting them in a child scope. – Mike Zboray Dec 05 '18 at 18:56
  • @downvoter, care to comment on how this question can be improved? Thanks – Guilherme Dec 05 '18 at 19:31

1 Answers1

1

So, after some discussion and links in the comments, the answer is NO. The braces { } does not help GC at all, even when they remove variables from the scope.

With that said, the GC can clean up memory that is still in the scope, and the { } braces are pure lexical. Doing GC.Collect(); may clean up the variables even when they are in a valid context (and, of course, will not be used anymore in the code). This is not necessary in most cases, since the GC will do this automatically when needed.

Guilherme
  • 5,143
  • 5
  • 39
  • 60
  • That’s not the full story. When local variables have a limited scope, i.e. a `{ }` block, then new local variables declared after that block may reuse their memory location, so in that regard, a limited scope *may* help the garbage collection, though it’s usually not important in practice. It may have a higher impact when debugging. – Holger Dec 06 '18 at 09:37