【问题标题】:Will these variables be garbage-collected?这些变量会被垃圾收集吗?
【发布时间】:2016-07-17 19:20:14
【问题描述】:

我今天正在练习我的编码技巧,今天解决了“从链表中删除某个值的所有元素”的问题。我想出的解决方案是

public void RemoveAll ( T val ) 
{
    if(_root == null)
       return;

    if(_root.Value == val)
    {
       _root = _root.Next;
       RemoveAll(val);
    }    

    Node last = _root, 
          cur = _root.Next;  
    while(cur != null)
    {
       if(cur.Value == val)
           last.Next = cur.Next;
       else
           last = cur;
       cur = cur.Next;
    }
}

这是我的问题:

cur.Value == val 我正在做一些事情,比如从

更改列表
A -> B -> C

A -> C

编译器或运行时环境是否会看到B 不再使用并丢弃它?还是我应该明确地这样做?

我有第二个问题,即调用堆栈是否会因递归 void 方法而爆炸。正如您在此处看到的,该方法有可能会调用自身。但是既然它是一个不返回值的方法,那么运行时环境不能只是擦除关于最后一次调用的数据吗?它没有理由留在记忆中(对吧?)。

【问题讨论】:

  • 应该最终...
  • 你会如何明确地做到这一点? B=null 还是什么?但是 B 只是指向 null 并且对象仍在内存中。
  • 当然,它将被收集,而不是丢弃。而且你迟早会炸毁堆栈,考虑一下 while (_root != null && _root.Value == val) _root = _root.Next;

标签: c# .net algorithm


【解决方案1】:

编译器或运行时环境是否会发现 B 不再使用并丢弃它?还是我应该明确地这样做?

GC,当它运行时,将意识到没有对该对象的活动引用并清理它(假设没有其他人持有对该对象的引用)。您不能手动清理 .NET 中的单个对象。在 .NET 中,内存由垃圾收集器根据需要进行管理和清理。

我有第二个问题,即调用堆栈是否会因递归 void 方法而爆炸。正如您在此处看到的,该方法有可能会调用自身。但是既然它是一个不返回值的方法,那么运行时环境不能只是擦除关于最后一次调用的数据吗?它没有理由留在记忆中(对吧?)。

您正在描述tail recursion. C# 编译器不会生成尾递归调用。因为如果你的递归太深,你可能会遇到StackOverflowException

该限制不是 CLR 限制 - .NET Framework 确实支持尾调用。它是不发出尾部 IL 操作码的 C# 编译器。当手动生成 IL 或使用 F# 时,您可以在 .NET Framework 中使用 Tail Recursion,它会在适当的时候生成尾调用。

更多详情请见https://stackoverflow.com/a/15865150/1163867

PS。我认为您的代码有错误。看起来您应该在递归调用 RemoveAll 后尽早返回:

if(_root.Value == val)
{
   _root = _root.Next;
   RemoveAll(val);
   return;
}    

【讨论】:

  • 根据您引用的链接,虽然 C# 编译器不会发出 tail 操作码,但 JIT 编译器仍然可以检测和优化尾递归。使用 C# 并不意味着您永远不会看到尾递归被优化掉,只是 C# 编译器本身不会帮助优化。这里更大的问题是递归调用后缺少return; 语句;恕我直言,您的回答应该强调这一点,因此很明显,关于尾递归的整个讨论是没有实际意义的,除非该方法在其递归调用后立即返回。
猜你喜欢
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2020-11-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多