Blender内部定义了自己内存泄漏检测机制,同时提供了了二套实现机制,函数整合于于bf_intern_guardedalloc库中:
mallocn_lockfree_impl.c和mallocn_guarded_impl.c分别实现了这二套内存分配机制,头文件定义于MEM_guardedalloc.h中,为方便实现二种机制的切换,函数头使用的是函数指针方式,以便于动态切换。
mallocn_lockfree_impl.c中的实现用于正常工作模式,mallocn_guarded_impl.c的实现用于调试模式,提供更多信息,更方便定位内存问题。
mallocn.c中实现函数指针被赋值,默认使用mallocn_lockfree_impl.c中的函数,如果需要使用mallocn_guarded_impl.c中的函数,只需要调用MEM_use_guarded_allocator函数,实现二套机制的切换。
在blender主程序文件中createor.c中,只有当明确指明调试模式时使用guarded模式内存分配,以方便理准确定位内存问题
1 /* NOTE: Special exception for guarded allocator type switch: 2 * we need to perform switch from lock-free to fully 3 * guarded allocator before any allocation happened. 4 */ 5 { 6 int i; 7 for (i = 0; i < argc; i++) { 8 if (STR_ELEM(argv[i], "-d", "--debug", "--debug-memory", "--debug-all")) { 9 printf("Switching to fully guarded memory allocator. "); 10 MEM_use_guarded_allocator(); 11 break; 12 } 13 else if (STREQ(argv[i], "--")) { 14 break; 15 } 16 } 17 MEM_initialize_memleak_detection(); 18 }
同时在第17行调用MEM_initialize_memleak_detection()函数实例化MemLeakPrinter(leak_detector.cc中定义)类到静态变量中,用于初始内存泄漏检测。该类只定义了析构函数,因此在程序结束时自动释放时调用析构函数,实现打印内存泄漏信息。也就无需主动调用,正发布版程序中也无需处理,不用担心性能。
例如:MEM_guardedalloc.h中定义的函数指针:extern void *(*MEM_callocN)(size_t len, const char *str) ,在mallocn.c中将它赋值
void *(*MEM_callocN)(size_t len, const char *str) = MEM_guarded_callocN;
MME_lockfree_callocN函数定义于mallocn_guarded_impl.c文件中,如下:
1 void *MEM_guarded_mallocN(size_t len, const char *str) 2 { 3 MemHead *memh; 4 5 len = SIZET_ALIGN_4(len); 6 7 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail)); 8 9 if (LIKELY(memh)) { 10 make_memhead_header(memh, len, str); 11 if (UNLIKELY(malloc_debug_memset && len)) { 12 memset(memh + 1, 255, len); 13 } 14 15 #ifdef DEBUG_MEMCOUNTER 16 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) 17 memcount_raise(__func__); 18 memh->_count = _mallocn_count++; 19 #endif 20 return (++memh); 21 } 22 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u ", 23 SIZET_ARG(len), 24 str, 25 (unsigned int)mem_in_use); 26 return NULL; 27 }
通过代码分析可知,内存分配的保护措施就是,在请求分配内存块时,在前面额外增加了一个MemHead和MemTail,它存储了请求的内存块大小,同时更新totblock,mem_in_use,peak_mem三个与内存保护相关变量
以下是Mem_freeN指向的函数MEM_gurded_freeN(它与Mem_allocN相对应,用于释放内存)
1 void MEM_guarded_freeN(void *vmemh) 2 { 3 MemTail *memt; 4 MemHead *memh = vmemh; 5 const char *name; 6 7 if (memh == NULL) { 8 MemorY_ErroR("free", "attempt to free NULL pointer"); 9 /* print_error(err_stream, "%d ", (memh+4000)->tag1); */ 10 return; 11 } 12 13 if (sizeof(intptr_t) == 8) { 14 if (((intptr_t)memh) & 0x7) { 15 MemorY_ErroR("free", "attempt to free illegal pointer"); 16 return; 17 } 18 } 19 else { 20 if (((intptr_t)memh) & 0x3) { 21 MemorY_ErroR("free", "attempt to free illegal pointer"); 22 return; 23 } 24 } 25 26 memh--; 27 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) { 28 MemorY_ErroR(memh->name, "double free"); 29 return; 30 } 31 32 if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) { 33 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len); 34 if (memt->tag3 == MEMTAG3) { 35 36 if (leak_detector_has_run) { 37 MemorY_ErroR(memh->name, free_after_leak_detection_message); 38 } 39 40 memh->tag1 = MEMFREE; 41 memh->tag2 = MEMFREE; 42 memt->tag3 = MEMFREE; 43 /* after tags !!! */ 44 rem_memblock(memh); 45 46 return; 47 } 48 MemorY_ErroR(memh->name, "end corrupt"); 49 name = check_memlist(memh); 50 if (name != NULL) { 51 if (name != memh->name) { 52 MemorY_ErroR(name, "is also corrupt"); 53 } 54 } 55 } 56 else { 57 mem_lock_thread(); 58 name = check_memlist(memh); 59 mem_unlock_thread(); 60 if (name == NULL) { 61 MemorY_ErroR("free", "pointer not in memlist"); 62 } 63 else { 64 MemorY_ErroR(name, "error in header"); 65 } 66 } 67 68 totblock--; 69 /* here a DUMP should happen */ 70 }
只要通过该库中函数分配的内存就能通过设置启动参数,在程序结束时显示内存泄漏等相关情况。