为什么这个内存管理技巧有效?

10

指的是Unity文档中的这一部分:

大堆,垃圾回收缓慢但不频繁

    var tmp = new System.Object[1024];

    // make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks
    for (int i = 0; i < 1024; i++)
        tmp[i] = new byte[1024];

    // release reference
    tmp = null;

这个技巧的关键在于程序启动时预分配一些内存块。

为什么这个技巧会起作用呢?

这些块是否在被预分配时被“注册”(或“绑定”)到应用程序中,以至于即使当Start()结束并释放tmp时,操作系统仍将这些块视为已“注册”到该应用程序中? 由于这些块已经“注册”到应用程序中,因此应用程序的堆大小扩展到了某个大小,下次获取内存块时,操作系统将直接从该应用程序的堆中选择。

我的解释正确吗?不管是不是,请有人详细解释一下,谢谢。

2个回答

5
这不是一个技巧,而是Unity3D中的部分处理内存的方式。
在Unity3D中,由Mono处理并将被垃圾回收的对象和由Unity处理并不会被垃圾回收的对象。字符串、整数等由Mono自动清理,我们不必担心这个问题。纹理(2D)等则不是如此,我们必须手动处理这些对象。
当请求内存时,首先发生的事情是内存管理器扫描应用程序当前从操作系统分配的内存,以查找足够大以存储您正在请求的数据的块。如果找到匹配项,则使用该内存。如果未找到匹配项,则应用程序将请求来自操作系统的额外内存以存储您的数据。当不再使用此数据时,它将被垃圾回收,但应用程序仍保留该内存。实际上,它对内存设置了一个标志,表示它是“可用的”或可重新分配的。通过这种方式减少向操作系统请求内存的次数,从而使其永远不会返回。
这意味着两件事:
1)您的应用程序的内存将继续增长,并且不会将内存返回给操作系统。在移动设备上,这很危险,因为如果使用的内存过多,应用程序将被终止。
2)实际上,您的应用程序可能分配了比它实际需要的内存多得多。这是由于内存碎片造成的。您的应用程序的内存池中可能有10MB可用内存,但没有一个块足够大以容纳您需要存储的数据。因此,应用程序可能会从操作系统请求更多内存,因为没有可用的连续内存块可供使用。
因为您正在创建一个大对象并因此请求内存,所以当您将该对象设置为null并向垃圾回收器发信号表明应用程序不再需要该内存时,重新分配该保留内存以供其他对象使用比从操作系统请求额外内存更快。这就是为什么理论上这种特定方法快速且会导致性能峰值较低的原因,因为垃圾回收器调用次数较少,特别是对于大的连续内存分配。

4

为什么这个技巧会生效?

这个技巧可以生效是因为应用程序只有在操作系统内存管理器内存不足并明确要求释放才会将内存返回给操作系统,此时它们会尽可能地释放内存。我们假设一旦分配了内存,就会再次需要它。如果已经分配了内存,除非真的需要使用它,否则没有理由将其返还给操作系统。


@downvoter - 能否详细说明您看到的问题在哪里? - Yuval Itzchakov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接