2

我正在尝试实现一种简单的编程语言。我想让它的用户不必管理内存,所以我决定实现一个垃圾收集器。在检查了一些材料后,我能想到的最简单的方法是这样的:

有两种堆区域。第一个用于存储大对象(大于 85,000 字节),另一个用于存储小对象。下面我使用 BZ 作为第一个,SZ 作为第二个。

BZ 使用标记和扫描算法,因为移动大物体的成本很高。我不压缩,所以会有碎片。

SZ 使用带有标记紧凑的代。分三代:0、1、2。分配请求直接到0代,当0代满了,我会在上面做垃圾回收,存活的会提升到1代。1代和2代会满时也进行垃圾收集。

虚拟机启动时会从OS分配一块大内存作为虚拟机中的堆区BZ和SZ中的每一代都会占用固定的一部分内存,当分配请求不能满意,虚拟机会报错OTM(out of memory)。这有一个问题:当虚拟机启动时,即使让程序在其上运行也应该只需要一点内存,但它仍然使用很多。更好的方法是让虚拟机从操作系统获得少量内存,然后当程序需要更多内存时,虚拟机将从操作系统获得更多。我准备在SZ中给第2代分配一个更大的内存,然后把第2代的东西全部复制到新的内存区。并为 BZ 做同样的事情。

另一个问题发生在 BZ 已满而 SZ 为空时,即使我们实际上有足够的空闲堆大小用于 SZ 中的大对象,我也无法满足大对象分配请求。如何处理这个问题?

4

1 回答 1

4

我正在尝试了解您的方法。由于您没有完全提及您的策略,因此我有一些假设。

注意:以下是我的假设分析,实际上可能不太可能。因此,如果您没有时间,请跳过答案。

您正在尝试使用带有更改的 Generational GC;有2种分类

(1) 大尺寸物体BZ

(2)小尺寸物体SZ

SZ使用压缩(CMS)执行分代 GC

从上面的理解我们知道SZG2有长寿命的对象。我预计 szG2 中的 GC不像SZG1 或 SZG0那样频繁,因为长寿命的对象通常寿命更长,所以更少的死收集和SZG2的大小会随着时间的推移而增加失效,因此 GC 需要花费大量时间遍历所有元素,因此与SZG1 或 SZG0相比,在SZG2上进行频繁的 GC 效率较低(长时间的 GC 峰值,因此对用户来说有明显的延迟) 。

同样,对于BZ,可能需要大量内存(因为大对象占用更多空间)。所以为了解决你的查询

"The other problem occurs when the BZ is full and SZ is empty, I would be silly not be able to satisfy a big object allocation request even though we in fact have enough free heap size for the big object in SZ. How to deal with this problem?"

既然您说“当程序需要更多内存时,虚拟机将从操作系统中获得更多”

我有一个小想法,可能没有生产力或可能无法实现,并且完全取决于您对 GCHeap 结构的实现。让您的虚拟机按如下方式分配内存

在此处输入图像描述

下面的可能性(我从“程序的内存段”中借用了想法,如下所示)在低级别是可能的。

在此处输入图像描述

如上图所示,必须以SZG0BZ相互扩展的方式定义GCHeap 结构。为了实现图 a图 b中提到的 GCHeap 结构,我们需要在区域SZG[0 -2]大小和BZ

因此,如果您想将应用程序的堆划分为多个堆,那么您可以将图 A堆放在图 B上以减少碎片(当我说碎片时it means "when the BZ is full and SZ is empty, I would be silly not be able to satisfy a big object allocation request even though we in fact have enough free heap size for the big object in SZ.")。

所以有效的结构将是

            B
            |
            B
            |
            B
            |
            B
            |
            A

现在它完全取决于启发式来决定是在多个 GCHeap 结构中考虑 GCHeap 数据结构,如 GCHeapA、GCHeapB 还是根据需求将其作为单个堆。

如果您不想有多个堆,那么您可以使用图 A 并进行小幅修正Setting base address of **SZG2** to top of heap

图 a 背后的关键原因如下:我们知道SZG0经常被 GC'ed,因此与SZG1SZG2 相比,它可以有更多的空闲空间,因为死对象被删除,幸存对象被移动到SZG1SZG2。所以如果分配BZ越多,它就可以向SZG0增长。

图中, SZG1SZG2的基地址是 连续的,因为SZG2更容易出现内存不足错误,因为老一代对象往往寿命更长,而且 GC 不会扫描太多(注意:这只是我的假设和意见)所以SZG2一直向外绑定。

于 2013-02-27T17:46:07.063 回答