首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >孤立对象的垃圾收集(树节点)适用于“release”,但在VS-调试器中不起作用。

孤立对象的垃圾收集(树节点)适用于“release”,但在VS-调试器中不起作用。
EN

Stack Overflow用户
提问于 2011-04-04 23:00:09
回答 1查看 2.4K关注 0票数 3

情况

根据这个accepted answer,如果"GC“看到两个或多个对象的循环引用,而这些对象没有被任何其他对象或永久GC句柄引用,那么这些对象将被收集起来。

我想知道垃圾收集是否适用于一个超级简单的树结构,它甚至没有内容,只有带有父引用和子引用的树节点。

假设您创建了一个根节点,向它添加了一个子节点,然后向子节点添加了一个子节点,等等,这样就不是真正的树,而是更像一个列表(每个节点最多有一个子节点和一个父节点)。

然后,如果我们删除根的子节点和这个子树中所有对节点的引用,就像我理解上面的答案一样,垃圾收集器应该清理子树。

问题描述

如果您查看下面测试代码中的主方法,当从发布目录运行exe时,我会看到内存消耗增加到~1GB,然后下降到~27 to (在1. GC.collect之后),然后再次下降到~27 to (对于2.GC.collect)。

现在,当在调试器中运行它时,用于执行此操作的OutOfMemoryException内存消耗会上升到1GB,而对于1.GC.collect内存消耗则保持在原来的1.6GB水平,第二个循环需要很长时间,然后我最终在第二个for -循环中得到一个

问题

为什么在调试器中出现这种行为?

在调试过程中,垃圾收集不也应该工作吗?我是不是遗漏了一些关于调试器的信息?

旁注

  • 我正在使用VisualStudio2010Express版
  • ,我只在这里调用GC.Collect(),以确保垃圾收集应该已经进行。(我不打算正常使用)

代码语言:javascript
复制
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();
    }
  }
}

我只包含以下代码,以防您想要自己测试它,它并不真正有用

代码语言:javascript
复制
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);
    }

  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-04-04 23:32:27

这是故意的。当附加调试器时,方法中的对象引用的生存期将延长到该方法的末尾。这对于简化调试是很重要的。您的TreeNode类既保留了对父类的引用,也保存了对其子类的引用。因此,任何对树节点的引用都会保持对整个树的引用。

包括子引用,它将保留树的“删除”部分。虽然在调用GC.Collect()时它不在作用域,但它仍然存在于方法的堆栈框架中。范围是语言特性,而不是运行时特性。如果没有调试器,抖动就会告诉垃圾收集器,在for循环结束时,子引用不再处于活动状态。因此,它的引用节点可以被收集。

注意,当您显式地将子对象设置为null时,不会得到OOM:

代码语言:javascript
复制
  for (int i = 0; i < 15000000; i++)
  {
    TreeNode child = new TreeNode(node); 
    node = child;
    child = null;
  }

不要写那种代码,你已经做了一个非常人工的例子。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5545288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档