对于“检测内存错误”这一用法,我们可以用 valgrind 或者 dmalloc 或者 efence 来达到相同的目的,专业的除错工具比自己山寨一个内存检查器要靠谱。...

news/2024/7/20 13:43:37 标签: 内存管理, python, c/c++
对于“检测内存错误”这一用法,我们可以用 valgrind 或者 dmalloc 或者 efence 来达到相同的目的,专业的除错工具比自己山寨一个内存检查器要靠谱。

不过我确实自己重写了 malloc 的宏,比较方便好用,源文件也很短.

 

--------------------------------------------------
在应用程序中替换Linux中Glibc的malloc的四种方法
http://blog.csdn.net/littlefang/article/details/6052563

 

         打算优化系统的内存分配,接管glibc提供的内存管理,但是整个工程的代码量很大,使用malloc、realloc、calloc和free的地方到处都是,如果自己写好的接口需要重命名所有的调用,先不说工作量,部分没有权限查看代码的.a文件就搞不定了。所以需要替换掉系统的malloc,保证原有调用的名称不变。经过尝试,共有四种方法可以替换,各有优缺点吧。

方案1 使用环境变量LD_PRELOAD

         环境变量LD_PRELOAD指定程序运行时优先加载的动态连接库,这个动态链接库中的符号优先级是最高的。标准C的各种函数都是存放在libc.so.6的文件中,在程序运行时自动链接。使用LD_PRELOAD后,自己编写的malloc的加载顺序高于glibc中的malloc,这样就实现了替换。用法:

         [littlefang]$ LD_PRELOAD=" ./mymalloc.so"

         这是最实用的替换方法,动态链接库加载过程中提供了初始化函数,可以轻易的获得系统malloc的句柄,再将它做进一步的管理,Hoard(参见深入Linux的内存管理,关于PTMalloc3、Hoard和TCMalloc)的就是这样实现的。

方案2  malloc调试变量

         __malloc_hook是一组glibc提供的malloc调试变量中的一个,这组变量包括:

 

[cpp:nogutter] view plain copy print ?
  1. void *(*__malloc_hook)(size_t size, const void *caller);   
  2.   
  3. void *(*__realloc_hook)(void *ptr, size_t size, const void *caller);   
  4.   
  5. void *(*__memalign_hook)(size_t alignment, size_t size, const void *caller);   
  6.   
  7. void (*__free_hook)(void *ptr, const void *caller);   
  8.   
  9. void (*__malloc_initialize_hook)(void);   
  10.   
  11. void (*__after_morecore_hook)(void);  

 

只要你在程序中写上”__malloc_hook = my_malloc_hook;”,之后的malloc调用都会使用my_malloc_hook函数,方便易行。但是这组调试变量不是线程安全的,当你想用系统malloc的时候不得不把他们改回来,多线程调用就得上锁了。因此方案2不很适用于系统内存优化,勉强用来简单管理线程内存使用。

         详细用法请猛击这里

方案3 编译自己的libmalloc.a

         关于重载glibc的malloc库,ChinaUnix上有这样的讨论:

如果我用cc -o myprog myprog.c -lmylib, 而不想修改缺省的ld的命令行参数或者linker脚本,不知可不可以?

这个方法确实比较理想,只需要make一次就OK了,不用更改环境变量,省得担心后台运行的问题。后面有人回复让楼主试试,不知道楼主试了没有,我试了一下。

若要把系统内存管理起来,首先还是要向操作系统申请内存,这个问题对于LD_PRELOAD方案很简单,链接库加载时就可以把glibc中的malloc加载进来,以后直接调用就可以了,如:

real_malloc = dlsym(RTLD_NEXT, "malloc");

但是你如果使用自己编译的malloc库,在你调用dlsym这个函数时,dlsym会调用dlerror,dlerror会调用calloc,calloc要调用malloc,而你的malloc正在初始化等待dlsym返回中,于是死循环了。有人说,在调用没有初始化完毕的malloc时,返回NULL,我试了dlsym不认账,加载可耻的失败了。在满世界的寻找dlsym的替代品未果后,我把目光瞄住了tcmalloc(参见深入Linux的内存管理,关于PTMalloc3、Hoard和TCMalloc)。Tcmalloc使用时需要在链接时加上-ltcmalloc即可,它代码里也没使用dlsym,大略了看了下它的代码,它使用mmap从系统获取的内存。

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

这种方法从页面级就要对系统内存进行管理,Glibc中的malloc就是使用mmap和brk两个函数从程序堆中获得内存的。无疑,这比起用malloc分配的内存复杂了很多。

方案4 链接过程控制

ld中有一个选项 –wrap,当查找某个符号时,它优先先解析__wrap_symbol, 解析不到才去解析symbol。例如:

 

[cpp:nogutter] view plain copy print ?
  1. void *__wrap_malloc (size_t c)  
  2.   
  3. {  
  4.   
  5.      printf ("malloc called with %zu/n", c);  
  6.   
  7.      return __real_malloc (c);  
  8.   
  9. }  

 

当其它文件与你实现__wrap_malloc函数的文件链接时使用--wrap malloc ,则所有到malloc的调用都是会链接到__wrap_malloc上。只有调用__reall_malloc时才会调用真正的malloc。

 

[cpp:nogutter] view plain copy print ?
  1. #include <stdio.h>  
  2.   
  3. #include <stdlib.h>  
  4.   
  5.    
  6.   
  7. void *__real_malloc(size_t);  
  8.   
  9.    
  10.   
  11. void *__wrap_malloc(size_t c)  
  12.   
  13. {  
  14.   
  15.         printf("My MALLOC called: %d/n", c);  
  16.   
  17.         return __real_malloc(c);  
  18.   
  19. }  
  20.   
  21.    
  22.   
  23. int main (int argc, char *argv[])  
  24.   
  25. {  
  26.   
  27.         void *ptr = malloc(12);  
  28.   
  29.    
  30.   
  31.         return 0;  
  32.   
  33. }  

 

编译 

[littlefang]$ gcc wrap.c -o wrap -Wl,-wrap,malloc

运行 

[littlefang]$ ./wrap

My MALLOC called: 12

Gcc或g++编译使用 –Wl选项,以指定链接器参数,比如同时替换malloc,free,realloc就要用

gcc wrap.c -o wrap -Wl,-wrap,malloc  -Wl,-wrap,free  -Wl,-wrap,realloc。

特别需要注意的是,如果你的__wrap_malloc是用C++实现的,千万不要忘记加上extern “C”做修饰,不然会出现"undefine reference to __wrap_malloc"。

 

 

 

--------------------------------------------------

http://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/  "应用 Valgrind 发现 Linux 程序的内存问题 "

 

以下为转载,原文不知.

--------------------------------------------------

不要重载全局 ::operator new()
日期:2011年3月14日 | 来源:看文章 | 编辑9号小组 | 看文章

  总之,第二种重载方式看似功能更丰富,但其实与程序里使用的其他 C++ library 很难无缝配合。

  综上,对于现实生活中的 C++ 项目,重载 ::operator new() 几乎没有用武之地,因为很难处理好与程序所用的 C++ library 的关系,毕竟大多数 library 在设计的时候没有考虑到你会重载 ::operator new() 并强塞给它。

  如果确实需要定制内存分配,该如何办?

  替代办法

  很简单,替换 malloc。如果需要,直接从 malloc 层面入手,通过 LD_PRELOAD 来加载一个 .so,其中有 malloc/free 的替代实现(drop-in replacement),这样能同时为 C 和 C++ 代码服务,而且避免 C++ 重载 ::operator new() 的阴暗角落。

  对于“检测内存错误”这一用法,我们可以用 valgrind 或者 dmalloc 或者 efence 来达到相同的目的,专业的除错工具比自己山寨一个内存检查器要靠谱。

  对于“统计内存使用数据”,替换 malloc 同样能得到足够的信息,因为我们可以用 backtrace() 函数来获得调用栈,这比 new (__FILE__, __LINE__) 的信息更丰富。比方说你通过分析 (__FILE__, __LINE__) 发现 std::string 大量分配释放内存,有超出预期的开销,但是你却不知道代码里哪一部分在反复创建和销毁 std::string 对象,因为 (__FILE__, __LINE__) 只能告诉你最内层的调用函数。用 backtrace() 能找到真正的发起调用者。

  对于“性能优化”这一用法,我认为这目前的多线程开发中,自己实现一个能打败系统默认的 malloc 的内存分配器是不现实的。一个通用的内存分配器本来就有相当的难度,为多线程程序实现一个安全和高效的通用(全局)内存分配器超出了一般开发人员的能力。不如使用现有的针对多核多线程优化的 malloc,例如 Google tcmalloc 和 Intel TBB 2.2 里的内存分配器。好在这些 allocator 都不是侵入式的,也无须重载 ::operator new()。

  为单独的 class 重载 operator new() 有问题吗?

  与全局 ::operator new() 不同,per-class operator new() 和 operator delete () 的影响面要小得多,它只影响本 class 及其派生类。似乎重载 member operator new() 是可行的。我对此持反对态度。

  如果一个 class Node 需要重载 member operator new(),说明它用到了特殊的内存分配策略,常见的情况是使用了内存池或对象池。我宁愿把这一事实明显地摆出来,而不是改变 new Node 的默认行为。具体地说,是用 factory 来创建对象,比如 static Node* Node::createNode() 或者 static shared_ptr<Node> Node::createNode();。

  这可以归结为最小惊讶原则:如果我在代码里读到 Node* p = new Node,我会认为它在 heap 上分配了内存,如果 Node class 重载了 member operator new(),那么我要事先仔细阅读 node.h 才能发现其实这行代码使用了私有的内存池。为什么不写得明确一点呢?写成 Node* p = Node::createNode(),那么我能猜到 Node::createNode() 肯定做了什么与 new Node 不一样的事情,免得将来大吃一惊。

  The Zen of Python 说 explicit is better than implicit,我深信不疑。

  总结:重载 ::operator new() 或许在某些临时的场合能应个急,但是不应该作为一种策略来使用。如果需要,我们可以从 malloc 层面入手,彻底而全面地替换内存分配器。

  参考文献:

  [1] 侯捷,《池內春秋—— Memory Pool 的設計哲學與無痛運用》,《程序员》2002 年第 9 期。

posted on 2012-02-17 11:29 clq 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/-clq/archive/2012/02/17/2355504.html


http://www.niftyadmin.cn/n/1506828.html

相关文章

python中定义一个动物类_Python 在函数中定义一个类,并返回,算闭包吗?

python有一个很有意思的地方&#xff0c;就是def函数可以嵌套在另一个def函数之中。调用外层函数时&#xff0c;运行到的内层def语句仅仅是完成对内层函数的定义&#xff0c;而不会去调用内层函数&#xff0c;除非在嵌套函数之后又显式的对其进行调用。 x 99 def f1(): x 88 …

2021年3月23日【Jiawei_Z】ALTIUM Designer2019 Licence 重复问题怎么解决

2021年3月23日 ALTIUM Designer2019 安装的时候会弹出:your licence is already used on computer怎么解决&#xff1f; 分析原因&#xff1a;因为很多用户都是用的&#xff08;PoJie版本&#xff09;的ALTIUM DESIGNER,软件启动的时候 会进行ID校验 解决办法&#xff1a;在…

机器学习技法笔记-Lecture 2 Dual support vector machine

支持向量机的对偶问题。 上节讲到可以用非线性变换将xn映射到zn&#xff0c;在z空间上求解SVM。 那要求解的w的维度变成新的d&#xff0c;如果d很大&#xff0c;那么QP求解变难。 希望将问题转化成对等的&#xff0c;只用求解N个变量的问题 之前讲正则化的时候&#xff0c;&…

JAVA正则表达式,matcher.find()和 matcher.matches()的区别

1.find()方法是部分匹配&#xff0c;是查找输入串中与模式匹配的子串&#xff0c;如果该匹配的串有组还可以使用group()函数。 matches()是全部匹配&#xff0c;是将整个输入串与模式匹配&#xff0c;如果要验证一个输入的数据是否为数字类型或其他类型&#xff0c;一般要用mat…

2021年4月2日 【Jiawei_Z】Quartus13.1 安装遇到的几个坑

2021年4月2日------Quartus13.1 安装遇到的几个坑 网络上面 关于quartus 的安装教程非常非常多&#xff0c;虽然用quartus很多年了&#xff0c;安装的时候 也循规蹈矩的按照网上的教程一步一步的装起来了 出现了下面几个问题&#xff0c;都是没有安装和破解好导致的&#xff…

html网页框架代码实例_前端开发之VUE.js入门及使用实例

相关项目交付的前端框架改造&#xff0c;为方便大家尽快完成从JSP到VUE的技能提升过渡&#xff0c;现将VUE框架相关知识点整理成文&#xff0c;方便大家快速的学习上手。本文主要内容&#xff1a;前段框架是什么&#xff1b;什么是VUE&#xff1b;VUE实例&#xff1b;VUE安装与…

2021年5月1日 【Jiawei_Z】 今天是被重装系统和安装软件搞垮的一天!

2021年5月1日 今天是被重装系统和各种软件搞垮的一天 首先&#xff0c;是重新安装电脑系统 已经不在需要重新再做什么系统启动盘&#xff0c;可以到windows系统之家去下载安装包&#xff0c;基本是解压文件后&#xff0c;一键安装 http://win.hrbapjy.cn/xtjc.html 这是我的…

Python最牛逼内建函数之 zip()

Python内置函数——zip zip(iter1,iter2,...) 将很多迭代器的每一个元素组成一一对应的元组&#xff0c;然后生成一个迭代器每次迭代都返回一组前面生成的元组 >>> z zip([1,2,3],[4,5,6]) >>> z <zip object at 0x101a8c788> >>> next(z) (…