Сборка мусора потерянных объектов (деревьев-узлов) работает для "release-exe", но не для VS-отладчика - PullRequest
2 голосов
/ 05 апреля 2011

Ситуация

Согласно этому принятому ответу , если " GC" видит "циклическую ссылку на 2 или более объектов, на которые не ссылаются никакие другие объекты или постоянныеGC обрабатывает, эти объекты будут собраны. "

Я хотел бы знать, работает ли сборка мусора для очень простой древовидной структуры, которая даже не имеет контента, только узлы дерева с родительскими и дочерними элементами-Рекомендации.

Представьте, что вы создаете корневой узел, добавляете к нему дочерний элемент, а затем дочерний элемент к дочернему элементу и т. Д. Так что на самом деле это не дерево, а скорее список (каждый узел имеет не более одного дочернего элемента и одного родительского элемента)).

Если мы затем удалим дочерний элемент root и все ссылки на узлы в поддереве этого дочернего элемента, как я понимаю ответ выше, сборщик мусора должен очистить поддерево.

Описание проблемы

Если вы посмотрите на Main-метод в тестовом коде ниже, когда запускает exe из Release-каталога, я получаю поведение, которое я ожидаю потребление памяти увеличивается до ~ 1 ГБ, затем снова уменьшается до ~ 27 МБ (после 1. GC.collect) и затем снова до ~ 27 МБ (для 2. GC.collect).

Теперькогда запускает его в отладчике потребление памяти для этого увеличивается до ~ 1 ГБ, а для 1.GC.collect потребление памяти остается на том же уровне, что и тогда, до 1,6 ГБ с использованием второго цикла forвремя от времени я наконец получаю OutOfMemoryException во втором цикле for.

Вопросы

Почему я получаю такое поведение в отладчике?
Shouldn '• Работает ли сборщик мусора во время отладки, мне не хватает информации о отладчике?

Примечания

  • Я использую Visual Studio 2010 Express Edition
  • Я вызываю только GC.Collect () дляконкретные цели тестирования здесь, чтобы убедиться, что сборка мусора должна была произойти. (Я не планирую использовать это нормально)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;    

namespace Tree
{
  class Program
  {
    static void Main(string[] args)
    {

      TreeNode root = new TreeNode(null); // the null-argument is the parent-node

      TreeNode node = root;

      for (int i = 0; i < 15000000; i++)
      {
        TreeNode child = new TreeNode(node); 
        node = child;
      }

      root.RemoveChild(root.Children[0] );
      node = root;
      GC.Collect();

      for (int i = 0; i < 15000000; i++)
      {
        TreeNode child = new TreeNode(node);
        node = child;
      }
      root.RemoveChild(root.Children[0]);
      node = root;

      GC.Collect();

      Console.ReadLine();
    }
  }
}

Я включаю только следующий кодесли вы хотите проверить это сами, это не очень полезно


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tree
{
  class TreeNode
  {
    public TreeNode Parent { get; private set; }
    public List<TreeNode> Children { get; set; }

    public TreeNode(TreeNode parent)
    {
      // since we are creating a new node we need to create its List of children
      Children = new List<TreeNode>();

      Parent = parent;
      if(parent != null) // the root node doesn't have a parent-node
        parent.AddChild(this);
    }

    public TreeNode(TreeNode parent, List<TreeNode> children)
    {
      // since we are creating a new node we need to create its List of children
      Children = new List<TreeNode>();

      Parent = parent;
      if (parent != null) // the root node doesn't have a parent-node
        parent.AddChild(this);

      Children = children;
    }

    public void AddChild(TreeNode child)
    {
      Children.Add(child);
    }

    public void RemoveChild(TreeNode child)
    {
      Children.Remove(child);
    }

  }
}

1 Ответ

5 голосов
/ 05 апреля 2011

Это по замыслу. Время жизни ссылки на объект в методе расширяется до конца метода, когда отладчик подключен. Это важно для облегчения отладки. Ваш класс TreeNode хранит как ссылку на своего родителя, так и его потомков. Таким образом, любая ссылка на узел дерева сохраняет ссылку на все дерево.

Включая ссылку child , он сохраняет ссылку на удаленный раздел дерева. Хотя к моменту вызова GC.Collect () он больше не находится в области видимости, он все еще существует в кадре стека метода. Область действия - это функция языка, а не функция времени выполнения. Без отладчика джиттер сообщает сборщику мусора, что ссылка child больше не работает в конце цикла for. Таким образом, его ссылочные узлы могут быть собраны.

Обратите внимание, что вы не получите OOM, если для child явно указано значение null:

  for (int i = 0; i < 15000000; i++)
  {
    TreeNode child = new TreeNode(node); 
    node = child;
    child = null;
  }

Не не пишите такой код, вы сделали очень искусственный пример.

...