代码之家  ›  专栏  ›  技术社区  ›  dreamlax

什么是解释垃圾收集如何工作的简单方法?

  •  6
  • dreamlax  · 技术社区  · 16 年前

    我的注意力跨度很短,所以我无法通过 Wikipedia article .

    我知道垃圾收集有几种技术,但一种常见的技术是“可到达性”测试,其中对象的收集资格取决于根对象是否可以“到达”(据我所知,它是一个已知不需要收集的对象)。当你想知道某个物体是否可以到达时,你会怎么做?你怎么知道该往哪里看?

    显然,收集器必须知道所有分配的对象和根对象。它如何确定这些对象的可达性?

    7 回复  |  直到 9 年前
        1
  •  2
  •   Joey Gumbo    16 年前

    我会说,通过移动指针/引用。原则上,您只需查看一个对象是否仍然有指向它的引用(来自其他对象、当前执行代码的局部变量,…)。如果还没有,那么就不能再获得这个对象的引用(像Java这样的语言,至少在不能做指针欺骗的地方),所以把那个特定的对象扔掉通常是安全的。

    其他使用(或仍在使用)的方案是引用计数,其中每个对象都有一个引用计数器,每次有人引用该对象时,该计数器必须递增,每次有人丢失对该对象的引用时,该计数器必须递减。如果我没记错的话,Windows中的COM就是这样工作的。

    JAVA和.NET使用(除其他)代垃圾收集,其中每个对象最初被假定为非常快地死亡(代际假设)。然后,它会进行一些优化,以保持垃圾收集周期的快速性,从而不会对运行过多的程序造成干扰。在以前,GC在运行时锁定一个程序并不罕见,有时会锁定几秒钟。

    除此之外,GC通常只在内存不足时运行,也就是说,积累了太多的死对象,需要回收。这就是为什么大多数托管应用程序似乎比非托管应用程序浪费更多的内存,即使在许多情况下,大部分内存可以通过运行一次GC回收。

        2
  •  1
  •   KarstenF    16 年前

    垃圾收集器总是知道每个分配的对象,否则它可能无法检测到要删除的不可访问对象。这在内存分配过程中都会得到处理。这是有道理的,因为如果垃圾收集器 了解每一个对象,那么就有可能形成一组孤立的对象,即使是垃圾收集器也无法访问这些对象。基本上,这是内存泄漏。所以至少垃圾收集器必须知道每个分配的对象。

    这就引出了垃圾收集器如何确定哪些对象符合删除条件的问题。有各种各样的技术,但有两种是“标记和清除”和“参考计数”(它们经常出现在课本中),它们值得了解垃圾收集的基本思想。

    标记和清理涉及垃圾收集器遍历对象引用(从一组已知的不可收集对象开始)并标记它可以访问的每个对象(例如,在对象上设置标志)。当它耗尽了所有引用后,就可以在扫描阶段删除未标记的对象集。

    引用计数涉及到垃圾收集器,它为内存中的每个对象保留一个计数,记录有多少其他对象引用它。每次从另一个对象建立对该对象的引用时,此计数器都将递增;删除引用时,此计数器将递减。当计数器达到0时,对象不再被任何对象引用,垃圾收集器知道它可以被安全删除(此时它基本上无法访问-没有对象“知道”该对象)。

        3
  •  1
  •   Ankur    14 年前

    当我们创建对象并将该对象指定为空时,该对象就可以被垃圾收集器删除了…

        4
  •  1
  •   supercat    12 年前

    垃圾收集的一个简单(但普通)方法类似于在一个建筑中找到所有有价值的东西,并将其移到一个新的建筑中,然后炸毁旧的建筑并将其替换为空的建筑。为了尽量减少所需的物体移动量,系统可以使用一些不同大小的建筑物。所有新物体都放在一个小建筑物里。当它满了的时候,所有有用的东西都被复制到一个中等的建筑中,小的建筑被炸毁并被替换。如果一个小建筑中剩下的东西不到一个小建筑的价值,那么所有有用的东西都将被转移到一个大建筑中,然后中型建筑将被炸毁并被替换。如果大型建筑似乎积累了太多垃圾,所有有用的东西都将复制到另一个大型建筑,然后大型建筑将被炸毁和替换。尽管这座小建筑会被炸毁并被大量更换,但里面的很多东西都是垃圾,不需要复制。同样地,到中等建筑需要更换的时候,其中的大部分材料都将被废弃,因此只需要将一小部分转移到大建筑中。复制大楼里所有的东西会很昂贵,但这种操作不会经常发生。

        5
  •  0
  •   Maurice Perry    16 年前

    收集器必须知道全局变量区域以及每个线程的每个函数/方法/过程调用的激活帧中对象引用的位置。处理器寄存器也可以包含对象引用。 某些信息必须由编译器或虚拟机提供。

        6
  •  0
  •   Macke    16 年前

    从根对象(即在main()中的堆栈上的那些等效对象)开始,然后只跟踪每个指针/引用,并将每个对象标记为“可访问”。继续,直到标记完所有对象。其余的对象(尚未标记)是垃圾。

    另一种方法是进行引用计数,也就是垃圾收集,除了在每次指针写入和清理时都会进行标记(一旦对象无法访问,则其引用计数为零,因此会被销毁)。请注意,参考计数需要一个标记和扫描阶段(如上所述)来解决循环。(即相互依附但没有人关心的物体)

        7
  •  0
  •   oxbow_lakes    16 年前

    你可以认为一个运行程序(也许我应该是Java特有的) Threads . 虚拟机可以使用每个线程的根框架作为 root . 然后,它可以遍历从根引用的所有内容的可到达性树(加上根下的所有堆栈帧)。

    其他东西都找不到