Avoid memory leakage C #

3

I have a class to make a tree that can be traversed up and down, with reference to the parent node, something like this:

public class Nodo
{
  protected List<Nodo> Hijos...
  public Nodo Padre....

  public void Add(Nodo nodo)
  {
     nodo.Padre = this;
     Hijos.add(nodo)
  }
}

To delete a node I do the following

public void Borra(int index)
{
  if(index < Hijos.count){
    Hijos[index].Padre = null;
    Hijos.RemoveAt(index);
  }
}

Assuming I have a tree where the root node has two children and in turn the first node of the root has a nested child, something like this:

Raiz
|-Nodo_1
| |- Nodo_1_2
|-Nodo_2

When I delete "Node_1", will I have a memory leak? since "Node_1_2" refers to his father "Node_1"

I did not want to do a recursive function to eliminate all references to the parents of a deleted branch, since I could have large trees and I do not know if it would be an unnecessary task.

    
asked by Lato 19.09.2018 в 18:23
source

2 answers

2

You have to eliminate recursively, something like being

public void Eliminar(Nodo nodo){

    List<Nodo> temp = new List<Nodo>();

    foreach(Nodo item in nodo.Hijos){

       if(item.Hijos != null 
            && item.Hijos.Count > 0){
          Eliminar(item); 
       }

       item.Padre = null;
       temp.Add(item);
    }

    foreach(var item in temp){
      nodo.Hijos.Remove(item);
    }

}

the list temp is necessary since the foreach does not allow to be removed while iterating, therefore it is done in a separate loop

Check how in the loop that iterates the children recursively invokes Eliminar() in a recursive way so you can remove if the descendants have also children

    
answered by 19.09.2018 в 18:36
0

I did some tests and saw that (at least in this case) it is not necessary to delete the reference to the father, it seems that C # manages the resources very well.

I did the test in the following way:

To the node object I added two attributes to be able to identify it: Level and Index and added a destructor to verify that the node is removed

    ~Nodo()
    {
        System.Diagnostics.Trace.WriteLine("Matando hijo "+Index+" del nivel " + Nivel);
    }

Then I generate a tree, where the root node has three children and to the second son I add another child node and to this I add 10 more

Nodo root = new Nodo();
root.Add();
root.Add();
root.Add();

root[1].Add();
root[1][0].Add();
for(int i=0; i<10;i++)
    root[1][0].Add();

After deleting the second node, whoever has more depth, I see that each node is removed in the log by forcing the GC call:

root.Del(1);
GC.Collect();

Console:

Matando hijo 4 del nivel 3
Matando hijo 3 del nivel 3
Matando hijo 2 del nivel 3
Matando hijo 1 del nivel 3
Matando hijo 0 del nivel 3
Matando hijo 8 del nivel 3
Matando hijo 1 del nivel 1
Matando hijo 0 del nivel 2
Matando hijo 7 del nivel 3
Matando hijo 10 del nivel 3
Matando hijo 9 del nivel 3
Matando hijo 6 del nivel 3
Matando hijo 5 del nivel 3

To rule out errors, save the deleted node in another temporary variable and effectively when invoking the GC nothing appeared in the console

temp = root[1];
root.Del(1);
GC.Collect();

After I set the temp variable to null, I called the GC and it reappeared in console every node removed

temp = null;
GC.Collect();

Console:

Matando hijo 4 del nivel 3
Matando hijo 3 del nivel 3
Matando hijo 2 del nivel 3
Matando hijo 1 del nivel 3
Matando hijo 0 del nivel 3
Matando hijo 8 del nivel 3
Matando hijo 1 del nivel 1
Matando hijo 0 del nivel 2
Matando hijo 7 del nivel 3
Matando hijo 10 del nivel 3
Matando hijo 9 del nivel 3
Matando hijo 6 del nivel 3
Matando hijo 5 del nivel 3

The why

As it turns out that although the child nodes have a reference to the parent node, this reference is a weak reference (WeakReference) since the child node is in the Heap.

In the first test, when the 1 node is removed, there is no longer no one in the stack that references this, then the GC removes it together with all its descendants, although there are references between the node 1 and their children, these references are inside the Heap, therefore, they are candidates to be eliminated.

In test 2 the node reference 1 is being passed to the temp variable which if it is in the stack, therefore, the node 1 and their children will not be eliminated until temp equals null.

Here there is a better explanation of weak references

    
answered by 24.09.2018 в 23:18