我正在尝试实现一种简单的编程语言。我想让它的用户不必管理内存,所以我决定实现一个垃圾收集器。在查看一些材料之后我能想到的最简单的方法是这样的:
堆区有两种。第一个用于存储大对象(大于85,000字节),另一个用于存储小对象。在下面我使用BZ作为第一个,SZ作为第二个。
BZ使用标记和扫描算法,因为移动大对象很昂贵。我不紧凑,所以会有碎片。
SZ使用标记紧凑的世代。有三代:0,1和2.分配请求直接进入第0代,当第0代已满时,我将对其进行垃圾回收,幸存者将被提升为第1代。第1代和第2代将满员时也做垃圾收集。
当虚拟机启动时,它将从操作系统中分配一个大内存用作虚拟机中的堆区域BZ和SZ中的每一代都将占用内存的固定部分,并且当分配请求可以不满意,虚拟机会给出错误OTM(内存不足)。这有一个问题:当虚拟机启动时,即使让程序运行它也只需要一点内存,但它仍然使用很多。更好的方法是让虚拟机从操作系统获取少量内存,然后当程序需要更多内存时,虚拟机将从操作系统中获得更多内存。我将为SZ中的第2代分配更大的内存,然后将第2代中的所有内容复制到新的内存区域。并为BZ做同样的事情。
当BZ已满并且SZ为空时,会发生另一个问题,即使我们实际上已经为SZ中的大对象提供了足够的可用堆大小,我也无法满足大对象分配请求。如何处理这个问题?
答案 0 :(得分:4)
我想了解你的方法。由于你没有完全提及你的策略,我有一些假设。
注意:以下是我的假设分析,可能无法实现。如果您没有时间,请跳过回答。
您正在尝试使用Generational GC进行更改;有两种类型的分类
(1)大尺寸物体 BZ 和
(2)小尺寸物体深圳。
SZ 使用压缩(CMS)执行分代GC
从上面的理解我们知道 SZG2 具有长寿命的对象。我期待 szG2 中的GC不像 SZG1或SZG0那样频繁因为长寿命对象通常倾向于活得更长,所以较少的死亡集合和 SZG2 的大小将随着时间的推移而更多,因此GC需要花费大量时间遍历所有元素,因此要经常使用GC与 SZG1或SZG0 相比, SZG2 的效率较低(长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结构的实现。 让您的虚拟机分配内存,如下所示
下面的可能性(我从下面所示的“程序的记忆段”中借鉴)是可能的。
如上图所示图a GCHeap结构必须以 SZG0 和 BZ 相互扩展的方式定义。实施图a ,图b 中提到的GCHeap结构我们需要在 SZG [0-2] 区域中具有适当的内存增长惯例 BZ 。
因此,如果您想将应用程序堆分成多个堆,那么您可以将数字A 堆积在数字B 上以减少碎片(当我说碎片时{{1} }})。
如此有效的结构
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."
现在完全依赖于启发式方法来决定是否考虑GCHeapA,GCHeapB等多个GCHeap结构中的GCHeap数据结构,或者根据需要将其作为单个堆。
如果你不想拥有多个堆,那么你可以使用图{A B
|
B
|
B
|
B
|
A
进行小修正
图a背后的关键原因如下: 我们知道 SZG0 经常使用GC,因此与 SZG1 和 SZG2 相比,它可以拥有更多可用空间,因为死对象已被移除并存活对象移动到 SZG1 和 SZG2 。因此,如果 BZ 的分配更多,它可以向 SZG0 增长。< / p>
在数字中, SZG1 和 SZG2 的基地址很有说服力,因为 SZG2 更容易出局内存错误,因为老一代的对象倾向于活得更长,而且GC'ing没有多少扫描(注意:这只是我的假设和意见)所以 SZG2 保持向外。