我目前正在实现一个基本的垃圾收集器,目的是在程序结束时删除所有左侧动态分配的对象。我希望课程文档更清晰:
/**
* This basic garbage collector provides easy memory leak prevention.
* Just derive your class from utils::Object and the collector will
* care about freeing dynamically allocated objects. This basic
* implementation will just free all internal cached objects at the end
* of program. So allocated memory which was not freed manually will
* stay blocked up to this point.
*/
class GarbageCollector {
private:
// All object collector should care about (true means array)
std::map< Object*, bool > objects;
GarbageCollector();
/**
* Free left allocated memory.
*/
~GarbageCollector() {
std::map< Object*, bool >::iterator it = objects.begin();
int counter = 0;
while( it != objects.end() ) {
// save pointer and iterate to next because
// delete will remove obj from object set
Object* obj = it->first;
bool array = it->second;
++it;
++counter;
if( array ) {
delete[] obj;
}
else
delete obj;
}
if( counter )
std::cout << "GC: " << counter << " object(s) freed\n";
}
public:
/**
* @return Static collector instance.
*/
static GarbageCollector& getCollector();
void addObject( Object* obj );
void addArray( Object* obj );
void remove( Object* obj );
};
其他类将从中继承的基类Object
将已分配内存的指针添加到此gc:
class Object {
public:
void* operator new( std::size_t size ) {
void* ptr = malloc( size );
if( !ptr )
throw std::bad_alloc();
GarbageCollector::getCollector().addObject( static_cast<Object*>(ptr) );
return ptr;
}
void operator delete( void* ptr ) {
GarbageCollector::getCollector().remove( static_cast<Object*>(ptr) );
free( ptr );
}
void* operator new[]( std::size_t size ) {
void* ptr = malloc( size );
if( !ptr )
throw std::bad_alloc();
GarbageCollector::getCollector().addArray( static_cast<Object*>(ptr) );
return ptr;
}
void operator delete[]( void* ptr ) {
GarbageCollector::getCollector().remove( static_cast<Object*>(ptr) );
free( ptr );
}
};
这适用于新声明。但是如果尝试通过new []分配一个数组,程序就会崩溃。 Valgrind --leak-check = yes 给出了这个输出:
==3030== Invalid read of size 8
==3030== at 0x408305: utils::GarbageCollector::~GarbageCollector() (garbageCollector.cpp:40)
==3030== by 0x55A4820: __run_exit_handlers (exit.c:78)
==3030== by 0x55A48A4: exit (exit.c:100)
==3030== by 0x558A313: (below main) (libc-start.c:258)
==3030== Address 0x5b8e038 is 8 bytes before a block of size 144 alloc'd
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
==3030== Invalid free() / delete / delete[]
==3030== at 0x4C282E0: free (vg_replace_malloc.c:366)
==3030== by 0x409AE8: utils::Object::operator delete[](void*) (object.cpp:54)
==3030== by 0x408339: utils::GarbageCollector::~GarbageCollector() (garbageCollector.cpp:40)
==3030== by 0x55A4820: __run_exit_handlers (exit.c:78)
==3030== by 0x55A48A4: exit (exit.c:100)
==3030== by 0x558A313: (below main) (libc-start.c:258)
==3030== Address 0x5b8e038 is 8 bytes before a block of size 144 alloc'd
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
GC: 1 object(s) freed
==3030==
==3030== HEAP SUMMARY:
==3030== in use at exit: 144 bytes in 1 blocks
==3030== total heap usage: 8 allocs, 8 frees, 896 bytes allocated
==3030==
==3030== 144 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3030== at 0x4C28F9F: malloc (vg_replace_malloc.c:236)
==3030== by 0x409A59: utils::Object::operator new[](unsigned long) (object.cpp:45)
==3030== by 0x409B58: start() (main.cpp:49)
==3030== by 0x409C30: main (main.cpp:54)
==3030==
==3030== LEAK SUMMARY:
==3030== definitely lost: 144 bytes in 1 blocks
==3030== indirectly lost: 0 bytes in 0 blocks
==3030== possibly lost: 0 bytes in 0 blocks
==3030== still reachable: 0 bytes in 0 blocks
==3030== suppressed: 0 bytes in 0 blocks
==3030==
==3030== For counts of detected and suppressed errors, rerun with: -v
==3030== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 4 from 4)
我调试了程序,gc正试图删除new []返回的地址中的内存。你能告诉我我的错吗?
答案 0 :(得分:3)
您不能将delete[]
表达式与operator new[]
返回的指针一起使用。您必须直接使用operator delete[]
。
这是因为new[]
表达式有时会调整operator new[]
的结果,而delete[]
表达式会调整相反方向的参数所以:
new[]
表达式返回的指针,请使用delete[]
表达式将其释放。operator new[]
返回了指针,请通过拨打operator delete[]
将其释放。一般情况下,对于new
表达式/ operator new
/ delete
表达式/ operator delete
也是如此,但GCC可让您远离这一点。
这是一个技术问题,仅涉及崩溃。在评论中讨论了代码作为内存泄漏防护工具的用处。
找到一篇简短的论文。请注意,如果您拨打operator delete[]
而不是delete[]
,
Object*
并返回,只需将所有内容存储为void*
。答案 1 :(得分:0)
GarbageCollector::~GarbageCollector()
调用Object::operator[] delete
和Object::operator delete
,这会更改GarbageCollector::objects
地图,同时迭代它。首先尝试制作副本:
...
std::map< Object*, bool > objectsCopy = objects;
std::map< Object*, bool >::iterator it = objectsCopy.begin();
int counter = 0;
while( it != objectsCopy.end() ) {
...
答案 2 :(得分:0)
如果仅在程序结束时执行此操作,请禁止执行remove()调用以完全不删除它。该对象将在稍后被销毁,无论如何地图都将为空。