使用libumem定位memory leak和memory corruption(3)-续

我明白了,我明白了为什么在(3)中发现了两处内存泄漏!先回顾一下(3)中的第二处泄漏的call stack, 注意这个stack是经过C++ 的符号mangle的,可读性很差,我首先用gc++filt处理一下:

> 080a7300$<bufctl_audit ! gc++filt
            ADDR          BUFADDR        TIMESTAMP           THREAD
                            CACHE          LASTLOG         CONTENTS
         80a7300          80a4fc0    d073d324dabb8                1
                          8090390          8075064                0
                 libumem.so.1`umem_cache_alloc_debug+0x16c
                 libumem.so.1`umem_cache_alloc+0xe1
                 libumem.so.1`umem_alloc+0x3f
                 libumem.so.1`malloc+0×23
                 libstdc++.so.6`operator new(unsigned int)+0×25
                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&)+0×43
                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned int, unsigned int, unsigned int)+0x5a
                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int)+0×28
                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::assign (char const*, unsigned int)+0x3f
                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator= (char const*)+0x2e
                 Foo::Foo(int) +0x5e
                 main+0×44
                 _start+0×80

略显恐怖哦-:)不要怕,我已经把关键部分用粗体标记出来了,可见这个call stack对应了源代码里面的 m_IDname=buf; 使用一个指向字符串的指针为std::string赋值。std::string的 operator=(char const*)的默认行为是deep copy,所谓deep copy是指要自己分配一块内存用于存放要存储的字符串。

在(3)中libumeme报的第一出内存泄漏根据计算是8个字节,计算的方法我在(2)中介绍数据结构的时候已经提到过了,这里不再熬述。这8个字节中4个字节是虚表指针,另外4字节是std::string本身所占的内存,本质上就是一根指针。这根指针指向真正存储字符串的内存。问题就在这里!

libumem报的第一处内存泄漏并不包含真正存储字符串的那块buffer!!

我们来验证一下,首先我们知道,一共分配了100个Foo实例,释放了99个,那另外的99个实例的内存分配情况如何呢?
> ::umalog ! less

T-0.000000000  addr=809dfc0  umem_alloc_16
         libumem.so.1`umem_cache_free_debug+0×135
         libumem.so.1`umem_cache_free+0x3f
         libumem.so.1`umem_free+0xd5
         libumem.so.1`process_free+0xfd
         libumem.so.1`free+0×14
         libstdc++.so.6`_ZdlPv+0×21
         _ZN3FooD0Ev+0×36
         main+0x8f
         _start+0×80

T-0.000000685  addr=80a4f80  umem_alloc_48
         libumem.so.1`umem_cache_free_debug+0×135
         libumem.so.1`umem_cache_free+0x3f
         libumem.so.1`umem_free+0xd5
         libumem.so.1`process_free+0xfd
         libumem.so.1`free+0×14
         libstdc++.so.6`_ZdlPv+0×21
         libstdc++.so.6`_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b
         libstdc++.so.6`_ZNSsD1Ev+0x4d
         _ZN3FooD0Ev+0x1f
         main+0x8f
         _start+0×80

T-0.000001421  addr=80a4f80  umem_alloc_48
         libumem.so.1`umem_cache_alloc_debug+0x16c
         libumem.so.1`umem_cache_alloc+0xe1
         libumem.so.1`umem_alloc+0x3f
         libumem.so.1`malloc+0×23
         libstdc++.so.6`_Znwj+0×25
         libstdc++.so.6`_ZNSs4_Rep9_S_createEjjRKSaIcE+0×43
         libstdc++.so.6`_ZNSs9_M_mutateEjjj+0x5a
         libstdc++.so.6`_ZNSs15_M_replace_safeEjjPKcj+0×28
         libstdc++.so.6`_ZNSs6assignEPKcj+0x3f
         libstdc++.so.6`_ZNSsaSEPKc+0x2e
         _ZN3FooC1Ei+0x5e
         main+0×44
         _start+0×80

T-0.000002402  addr=809dfc0  umem_alloc_16
         libumem.so.1`umem_cache_alloc_debug+0x16c
         libumem.so.1`umem_cache_alloc+0xe1
         libumem.so.1`umem_alloc+0x3f
         libumem.so.1`malloc+0×23
         libstdc++.so.6`_Znwj+0×25
         main+0×30
         _start+0×80
上面显示了一个Foo实例从new到delete的完整的生命周期。一共牵涉到4次内存操作:分配Foo实例内存–>分配m_IDname所管理的用于真正存储字符串的内存–>释放字符串内存–>释放Foo实例。

我们来看看第二处泄漏内存的dump情况:
> 080a4fc0/16X
0x80a4fc0:                   2a           3a10bfd6         15                     15               0
(地址是80a4fd4 -> ) 206f6f46         656a624f        77207463        20687469    353d4449
                             babb0030         baddcafe        feedface        292f             80a7300
                             a91abbed

我用斜体标出了User Data Section的内容,老实讲我不记得 std::string内部在管理字符串是什么样的数据结构了,我也不想去挖代码。但是有一点注意了,注意我用蓝颜色标注的部分,这个部分的起始地址是 0x80a4fc0+0×14 = 0x80a4fd4。0x80a4fd4是什么?就是第一处内存泄漏的Foo实例中std::string的第一个4字节指针:

> 0809dfe0/8X  –> 0x0809dfe0 就是第一出泄漏buffer的起始地址
0x809dfe0: 10              3a10bff0        8050e30         80a4fd4          feedfabb
                fb1             80a3200         a91afaed

> 80a4fd4/S
0x80a4fd4:      Foo Object with ID=50

现在清楚了,上面蓝色部分的memory dump就是 "Foo Object with ID=50"的Ascii码。还记得字符’0′的ASCII是多少吗?是30!看看0xbb前最后的两个字节 0×30 0×00 正好是 ’0′和”。

好了,内存泄漏介绍完了,其实主要还是看经验,要了解自己的代码和数据结构,才能有效的使用像mdb这样比较底层的工具,有比较好的作用。下一篇我会介绍如何使用libumem+mdb处理 memory corruption。

 

你可能感兴趣的:(数据结构,String,cache,basic,存储,leak)