Blender内存管理库(bf_intern_guardedalloc)

Blender项目主要由c/C++编写的,模块较多,规模很大。由于C/C++语言很容易出现内存泄漏,为此Blender在底层代码中提供了内存泄漏检测机制,方便在需要时进入调试模式报告内存泄漏。

为实现内存泄漏检测,必须在内存分配时做些额外的记录,为此,提供了一组函数替换c/c++语言原生的内存分配函数。

 

额外的内存泄漏检测必然将影响程序的工作性能,解决的办法是,Blender提供了二组内存分配方法,一套提供额外的内存泄漏检测,用于调试,便于发现内存问题,一套用于正式工作,不影响性能。

为便于二套内存分配方法切换,Blender定义了一组内存分配指针函数,替换c/C++语言原生的内存分配函数来实现内存泄漏检测功能,代码(位于mallocn.c文件中)如下:

查看代码
/* NOTE: Keep in sync with MEM_use_lockfree_allocator(). */
size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len;
void (*MEM_freeN)(void *vmemh) = MEM_lockfree_freeN;
void *(*MEM_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN;
void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_reallocN_id;
void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id;
void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
void *(*MEM_calloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_calloc_arrayN;
void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_malloc_arrayN;
void *(*MEM_mallocN_aligned)(size_t len,
                             size_t alignment,
                             const char *str) = MEM_lockfree_mallocN_aligned;
void (*MEM_printmemlist_pydict)(void) = MEM_lockfree_printmemlist_pydict;
void (*MEM_printmemlist)(void) = MEM_lockfree_printmemlist;
void (*MEM_callbackmemlist)(void (*func)(void *)) = MEM_lockfree_callbackmemlist;
void (*MEM_printmemlist_stats)(void) = MEM_lockfree_printmemlist_stats;
void (*MEM_set_error_callback)(void (*func)(const char *)) = MEM_lockfree_set_error_callback;
bool (*MEM_consistency_check)(void) = MEM_lockfree_consistency_check;
void (*MEM_set_memory_debug)(void) = MEM_lockfree_set_memory_debug;
size_t (*MEM_get_memory_in_use)(void) = MEM_lockfree_get_memory_in_use;
unsigned int (*MEM_get_memory_blocks_in_use)(void) = MEM_lockfree_get_memory_blocks_in_use;
void (*MEM_reset_peak_memory)(void) = MEM_lockfree_reset_peak_memory;
size_t (*MEM_get_peak_memory)(void) = MEM_lockfree_get_peak_memory;

#ifndef NDEBUG
const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr;
#endif

MEM_guardedalloc.h中定义了相关内存分配函数,均为指针函数,通过指针函数赋值,来实现二种机制的切换。二套函数具体实现分别们于mallocn_lockfree_impl.c和mallocn_guarded_impl.c文件中,整合在bf_intern_guardedalloc静态库中。

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.\n");
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\n",
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\n", (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 }

 

只要通过该库中函数分配的内存就能通过设置启动参数,在程序结束时显示内存泄漏等相关情况。

 

posted @ 2020-10-06 11:28  平凡人  阅读(670)  评论(0编辑  收藏  举报