2013-05-06 55 views
4

我们在嵌入式系统环境中使用C++,基本上不希望任何种类的动态内存分配(例如,请参阅Resources for memory management in embedded application)我们不这样做的原因)。尽管如此,我们仍然不希望没有一些基于C++的好功能,如STL容器和std :: string。对于第一个,我们会在初始化时保留一个特定的大小,并且不会让容器超出其容量。对于后者(std :: string),我有点怀疑如何“安全”地使用它们,因为它们有时会在堆上分配内存。但我发现使用std :: string(以及其他堆分配对象)的情况很好:我会在堆栈上分配对象本身(在由{}定界的特定范围内因为我正在从C++谈话),并允许它们分配堆,只要它们在超出范围时实际上释放了所有保留的内存。允许堆在短暂范围内分配对象以确保内存碎片自由

我明白这种方法并不能保证内存碎片的自由,但我觉得如果手头的范围很短,实际上会在范围结束后产生一个连续的空闲内存。

我也有过怀疑,有可能是一个问题,当多个任务共享同一个堆,但仍然可用内存应在年底连续提供手头的范围都是短暂的(不会阻止为例)。另外,我认为只有一个任务被允许在堆上分配内存,而其他的则不能,如果这真的很重要的话。

我建议使用堆分配对象有效吗?有人有另一种策略(部分)启用动态内存分配而不会冒内存碎片风险吗?

+0

不知道你使用的系统很难回答。我在嵌入式Linux上开展了一项工作,即使内存很小(64MB)并且设备始终保持运行状态,也不会出现内存碎片问题。 – syam 2013-05-06 15:35:24

+1

@syam:64MB ==小?我认为64 * K * B在我的一些项目中很大:) – 2013-05-10 12:15:33

回答

5

过去我们在紧密嵌入式系统上完成了各种C++风格的动态内存分配。你只需要遵循一些规则,并注意短期和长期的缓冲区。首先,内存池是你的朋友 - 正如文章所述。另外,对于C++在帮助配对和控制结构方面喜欢做出的所有小的(< 64字节)分配,单元分配方案是必不可少的 - 不仅对于分段控制,还有性能。一个单元分配器预分配大量相同大小的内存单元(比如64字节),并将它们放在一个空闲堆栈上。随着内存分配,您将它们从免费堆栈中弹出并返回。因为所有大小都是相同的,所以只有块大小的内部碎片。因为完成后不需要加入内存,分配和释放是O(1)次。

其他一些规则:如果您需要进行长期存在的动态分配,那么在它之前不要有任何短期分配。首先分配大缓冲区,然后是小分区,这样内存不会分散。另一个系统是在堆的后面放置长期分配,在前面放置短期分配。我们也取得了成功。

您也可以使用多个堆(池)来分隔不同类型的分配。如果你有一些东西在代码的一部分中创建了一大堆短期分配,而另一部分则遵循不同的模式,请给他们一个不同的堆。

以上所有内容如果仔细遵循,将会阻止或限制碎片。另一种解决方案是使用可重定位的内存分配系统,其中低优先级线程可以重新排序内存以保持其持续不断。我也看到了这样做了几次 - 为0长期分散性交易一点点。

alloca也可以提供帮助,但是如果你没有遵循内存碎片预防方法,那么你也只能分散堆栈 - 因为这往往是嵌入式土地中更有价值的资源,所以这可能不会是个好主意。

+0

这是什么有时被称为“池分配器”?我想知道它是否可以帮助解决这类问题。 – JBL 2013-05-06 15:40:30

+0

我忘记了我们确实更喜欢将内存池用于长寿命对象。我绝对是要求短命的对象。我感谢你的答案,因为它给了我很多想法,但我仍然错过了确认我的基于范围的方法是有效的。 – 2013-05-06 15:55:46

+1

基于范围的内存工作,只要你不在你的“范围”分配上混合使用同一个堆/池上的长期分配。这是我能给你的最好证实。 – 2013-05-06 17:43:14

1

您可能会考虑使用不太流行的alloca函数在堆栈上分配可变大小的变量。这样就不会出现碎片,但是如果对大变量使用alloca,则可能会遇到堆栈溢出。

编辑:关于从GNU C library docs分配的一些信息。