我目前正在使用Boehm垃圾收集器在C ++中使用大型应用程序。虽然它有效,但在我看来,GC对于我的目的来说是过度的(我不喜欢将它作为一种依赖,我不得不在我所做的每件事情中不断考虑和考虑GC,以免踩到它的脚趾)。我想找到一个更适合我需求的更好的解决方案,而不是一个覆盖它的全面解决方案。
在我的情况下,我有一个特定的类(以及从该类继承的所有内容),我想“收集”。我不需要一般的垃圾收集,除了这个特殊的类,我可以很容易地管理自己的内存。
在我开始使用GC之前,我使用了引用计数,但参考周期和频繁更新使得这不是理想的解决方案。
我有更好的方法来跟踪这门课程吗?其中一个不涉及额外的库依赖,如boost。
修改 如果我对我的物体的潜在寿命给出一个概述,那可能是最好的。
函数创建我的类的新实例,可能(或可能不)使用它。无论如何,它将此新实例作为返回值传递回调用方。调用者可能(或可能不)也使用它,并再次将其传递回堆栈,最终进入顶级函数,只是让指针淡入遗忘。
我不能只删除顶层的指针,因为“可能使用”的一部分涉及将指针传递给其他函数,这些函数可能(或可能不)存储指针以供将来某个地方使用。
我希望这能更好地说明我想解决的问题。我目前用Boehm垃圾收集器解决它,但如果可能的话,我希望更简单,非依赖,包括解决方案。
答案 0 :(得分:1)
在嵌入式系统世界或实时事件关键程序中,垃圾收集不受欢迎。使用动态内存的意义不大。
使用动态内存分配时,会发生碎片。垃圾收集器用于定期安排内存以减少碎片,例如组合顺序释放块。主要问题是何时执行此碎片整理或运行GC。
一些建议的替代方案:
重新设计系统以避免动态内存分配。
分配静态缓冲区并使用它们。例如,在RTOS系统中,为消息预分配空间,而不是动态分配它们。
使用堆栈,而不是堆。 如果可能,将堆栈用于动态分配的变量。如果变量需要超出函数执行的生命周期,这不是一个好主意。
对可变大小的数据设置限制。
与静态缓冲区一起,对可变长度数据或未知大小的传入数据设置限制。这可能意味着当输入无法停止时,必须暂停传入数据或多次缓冲。
创建自己的内存分配器。
创建许多分配不同大小的块的内存池。这将减少碎片。例如,对于小块,可以使用bitset
来确定正在使用哪些字节以及哪些字节可用。也许需要另一个64字节块的池。一切都取决于您的系统需求。
答案 1 :(得分:0)
如果您真的只需要对与单个类关联的内存分配进行特殊处理,那么您应该考虑重载该类的新运算符。
class MyClass
{
public:
void *operator new(size_t);
void operator delete(void *);
}
您可以实现这些运算符来执行跟踪内存所需的任何操作:从特殊池中分配它,在链接列表上放置引用以进行跟踪等。
void* MyClass::operator new(size_t size)
{
void *p = my_allocator(size); // e.g., instead of malloc()
// place p on a linked list, etc.
return p;
}
void MyClass::operator delete(void *p)
{
// remove p from list...
my_free(p);
}
然后,您可以编写外部代码,该代码可以遍历您要保留的列表,以检查每个当前分配的MyClass实例,GC实例适合您的情况。
答案 2 :(得分:0)
有了记忆,你应该总是尝试拥有明确的所有权和终身知识。生命周期决定你从哪里获取内存(和其他因素一样),即生活范围的堆栈,重用的池等等。所有权将告诉你何时以及是否释放内存。在您的情况下,GC拥有所有权并决定何时免费。通过引用计数,包装类可以实现此逻辑。如果使用手动内存管理,则不明确的所有权会导致难以维护代码。你必须避免在免费,双重释放和内存泄漏后使用。
要解决您的问题,找出谁应该保留所有权。这将决定使用的算法。 GC和ref计数是流行的选择,但有很多。如果所有权不明确,请将其交给第三方,该第三方的工作是跟踪它。如果共享所有权,请确保所有各方都可以通过专门的类强制执行它。这也可以通过简单的约定来强制执行,即foo类型的对象永远不应该在内部保留类型bar的ptrs,因为它们不拥有它们,如果它们这样做,则它们不能认为它们始终有效并且可能必须首先检查有效性。等
如果您发现这很难确定,则可能表明代码非常复杂。可以用更简单的方式制作吗?
了解如何使用和访问内存是编写用于维护和性能优化的干净代码的关键。无论使用何种语言,都是如此。
祝你好运。