【问题标题】:Garbage Collection垃圾收集
【发布时间】:2011-01-21 22:36:48
【问题描述】:

我无法理解垃圾收集的一些内容。

首先,数据是如何分配空间的?即在堆栈或堆上(据我所知,所有静态或全局变量都在堆栈上分配空间,局部变量在堆上分配空间)。

其次,GC 运行在堆栈或堆上的数据上?即像 Mark/Sweep 这样的 GC 算法会将堆栈上的数据称为根集,对吗?然后通过检查堆上的哪些变量引用根集来映射堆上所有可达的变量。

如果程序没有全局变量怎么办?那么算法是如何工作的呢?

问候, 小黑子

【问题讨论】:

标签: language-agnostic garbage-collection computer-science


【解决方案1】:

您可能会发现Garbage Collection on the Memory Management Reference 的简短摘要很有用。

最终,垃圾收集必须从处理器的寄存器开始,因为处理器无法访问的任何对象都可以被回收。根据语言和运行时系统,静态假设线程的堆栈和寄存器以及“全局变量”也是可访问的是有意义的。

堆栈可能会为您提供局部变量。因此,在简单的 GC 中,您首先要扫描线程上下文、它们的堆栈和全局变量。但这肯定不是在所有情况下都是正确的。有些语言不使用堆栈或具有全局变量。更重要的是,GC 可以使用barrier,这样他们就不必每次都查看每个堆栈或全局。一些专用硬件,例如 Symbolics Lisp Machine 在寄存器上有障碍!

【讨论】:

    【解决方案2】:

    Richard and Carl 在他们的 .NET Rocks 中展示了 Windows 内存模型,包括 .NET 模型和 GC!档案:

    Jeffrey Richter on the Windows Memory Model

    【讨论】:

      【解决方案3】:

      阅读this article。这是对单处理器垃圾收集技术的一个很好的调查。它将为您提供有关 GC 的基本理解和术语。然后,继续阅读 Jones 和 Lins 的书“垃圾收集:自动动态内存管理算法”。与我上面提到的调查文章相反,这本书不能在网络上免费获得;你必须买它;但这是值得的。

      【讨论】:

        【解决方案4】:

        这可能有助于澄清您所询问的平台的 GC - JVM、CLR、Lisp 等。也就是说:

        先退一步,某些局部变量一般是在栈上分配的。但是,具体情况可能因语言而异。以 C# 为例,只有本地的Value Types 和方法参数存储在堆栈上。因此,在 C# 中,foo 将被分配到堆栈上:

        public function bar() { 
            int foo = 2;
            ...
        }
        

        或者,动态分配的变量使用堆中的内存。这在直觉上应该是有意义的,否则每次调用new 时堆栈都必须动态增长。此外,这意味着这些变量只能用作分配它们的局部函数中的局部变量,这当然不是真的,因为我们可以拥有(例如)类成员变量。再举一个 C# 的例子,在下面的例子中,result 是在堆上分配的:

        public class MyInt
        {         
            public int MyValue;
        }
        
        ...
        MyInt result = new MyInt();
        result.MyValue = foo + 40;
        ...
        

        现在考虑到这个背景,上的内存是垃圾回收的。堆栈上的内存不需要 GC,因为当当前函数返回时,内存将被回收。在高层次上,GC 算法通过跟踪在堆上动态分配的所有对象来工作。一旦通过new分配,该对象将被GC跟踪,并在它不再在范围内并且没有更多引用时收集。

        【讨论】:

        • 非常感谢。这使事情更容易理解
        • "以 C# 为例,只有值类型存储在堆栈中。" - 这并不完全正确。引用“C# In Depth”,第 52 页:“...变量的值存在于它被声明的位置 - 因此,如果您有一个带有 int 类型实例变量的类,则该变量的任何给定对象的值将始终位于其余部分对象的数据 - 在堆上...只有局部变量和方法参数存在于堆栈上..."
        • 对,我的意思是只有作为值类型的局部变量才会存储在堆栈中。我会更新答案以使其更清楚......
        • @Justin:当你说:“这在直觉上应该是有道理的,否则每次调用 new 时堆栈都必须动态增长。另外,这意味着只能使用这样的变量在分配它们的本地函数中,这当然不是真的”......你的意思是只针对 C# 吗?下面是一段用 Java 编写的代码: public class StringCheck { public static void main(String[] args) { callingOne();调用二(); } public static void callingOne() { Integer temp = new Integer("1"); }
        • public static void callingTwo() { System.out.println("在callingTwo中访问temp:"+ temp); } } 似乎不起作用。出现错误:StringCheck.java:18:找不到符号符号:变量临时位置:类 StringCheck System.out.println("Accessing temp in callingTwo:"+ temp);
        【解决方案5】:

        首先,数据是如何分配空间的? 即在堆栈或堆上(根据我的 知识,所有静态的或全局的 变量在堆栈上分配空间 和局部变量被分配空间 在堆上)。

        不,堆栈变量是方法调用和局部变量。调用方法时会创建堆栈帧,返回时弹出堆栈帧。

        Java 和 C# 中的内存是通过调用“new”在堆上分配的。

        其次,GC 运行在堆栈上的数据或 堆?即 GC 算法 标记/扫描将参考数据 堆栈作为根集对吗?然后地图 堆上的所有可达变量 检查堆上的哪些变量引用 到根集。

        GC 在堆上使用。

        标记和扫描不会被视为最先进的 GC 算法。 Java 和 .NET GC 现在都使用分代模型。

        如果一个程序没有 全局变量?如何 那么算法有效吗?

        “全局变量”在 Java 和 C# 等所有内容都属于一个类的语言中是什么意思?

        对象图的根是任意的。我承认我不知道它是如何选择的。

        【讨论】:

        • 非常感谢......这对我解释了很多。
        【解决方案6】:

        【讨论】:

        • 我无法访问这本书的内容。还是谢谢
        猜你喜欢
        • 1970-01-01
        • 2018-12-30
        • 1970-01-01
        • 1970-01-01
        • 2010-12-13
        • 2012-05-04
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        相关资源
        最近更新 更多