2

我想了解Java在 C++ 或 C 的情况下堆管理器或操作系统中的哪些数据结构会跟踪线程和进程使用的内存位置。一种方法是使用对象和内存地址的映射以及内存起始地址和内存中对象大小的反向映射。但是在这里它将无法及时满足新的内存请求O(1)。有没有更好的数据结构来做到这一点?

4

3 回答 3

2

请注意,非托管语言将通过系统调用分配/释放内存,通常不会自行管理。仍然不管什么抽象级别(操作系统到运行时),都必须处理这个问题:

一种方法称为伙伴块分配,通过Wikipedia上的示例进行了很好的描述。它基本上跟踪不同大小的内存中空间的使用情况(通常是 2 的倍数)。这可以通过许多具有智能索引的数组来完成,或者更直观地使用二叉树来完成,每个节点告诉某个块是否空闲,一个级别上的所有节点代表相同大小的块。

这会受到内部碎片的影响;随着事情的发展,您最终可能会导致数据分散而不是有效地合并,从而难以适应大数据。这可以通过更复杂的动态系统来解决,但伙伴块具有简单的优点。

于 2013-06-13T06:09:38.730 回答
1

操作系统在整体视图中跟踪进程的内存分配 - 4KB 页面或更大的“块”存储在某种形式的列表中。

在典型的 Windows 实现(微软的 C 运行时库)中——至少在最近的版本中,所有内存分配都是通过 HeapAlloc() 系统调用完成的。因此,每个堆分配都会传递给操作系统。操作系统是否真的跟踪每一个分配,或者只是保留一张“什么是免费的,什么是使用的”的地图是另一回事。据我了解,堆管理代码没有“当前分配”列表,只有已释放内存块的列表

在 Linux/Unix 中,C 库通常会避免在每次分配时调用操作系统,而是使用大量内存,并在每次分配时将其分成更小的部分。同样,在堆管理中没有跟踪分配的内存。

这是在过程级别完成的。我不知道有一个操作系统可以在每个线程级别区分内存分配(除了 TLS - 线程本地存储,但这通常是一个非常小的区域,在典型的堆代码管理之外)。

因此,总而言之:操作系统和/或 C/C++ 运行时实际上并没有保留所有已使用分配的列表 - 它保留了“已释放”内存的列表 [当另一个块被释放时,通常会“加入”前一个和下一个连续分配以减少碎片]。当分配器第一次启动时,它会被分配一个大块,然后将其分配为单个释放的分配。当发出请求时,块被分成多个部分,空闲列表成为剩余部分。当这个块不够用时,会使用底层操作系统分配来分割另一个大块。

每次分配都会存储少量元数据,其中包含诸如“分配了多少内存”之类的内容,并且在释放内存时会使用此元数据。在典型情况下,此数据存储在分配的内存之前。但是,如果不以其他方式了解分配,就无法找到分配元数据。

于 2013-06-13T07:34:19.937 回答
0

C++ 中没有自动垃圾收集。您需要为 malloc/new 堆内存分配调用 free/delete。这就是像 valgrind(检查内存泄漏)这样的工具派上用场的地方。还有其他一些概念,例如 auto_ptr 可以自动释放堆内存,您可以参考。

于 2013-06-13T06:47:30.617 回答