整理的参考文献,记不清了

1 背景简介

出现疑似”内存泄露”问题: malloc申请的内存,free释放以后没有归还操作系统,比如内存模块占用的内存为10GB,释放内存以后,通过TOP命令或者/proc/pid/status查看占用的内存有时仍然为10G,有时为5G,有时为3G, etc,内存释放的行为不确定。

2 malloc()/free(), mmap(), brk(), 用户程序-->glibc -->linux kernel之间的关系

1) malloc()/free() 是C语言中常用的两个内存 分配/释放 函数,但是ANSI C并没有指定它们具体应该如何实现。因此在各个系统级平台上(windows, mac, linux等等),函数的底层的内存操纵方式并不一样。
2) 在linux下,malloc()/free()的实现是由  glibc库 负责,它会根据一定的策略与系统底层通信(调用系统API),所以,用户程序并不会直接和linux kernel进行交互,而是交由glibc托管,提供默认的内存管理器。关系为:用户程序 ---->glibc---->linux kernel。
3) glibc使用了 ptmalloc 作为其内存管理器的实现。

* brk分配的内chunk list,只能从top开始线性向下释放。中间释放掉的chunk,无法归还给OS,而是链入到了bins/fast bins的容器中。

* mmap分配的内存,等于直接从物理内存中映射了一块过来,释放这块内存时,可以直接归还给OS。

* 对于request的一块内存,到底是由brk分配,还是由mmap分配,这是由glibc策略机制决定的。 有个threshold,可以调节这种策略,默认小于threshold由brk分配,大于等于则由mmap分配, threshold默认为128kb。glibc具体从那个版本开始支持动态调节threshold需要调查。默认下,在64位系统上,brk可以动态调整到从 128kb到32mb。调整策略基本可以概括为:发现对顶可以release的可用内存超过256kb的话,就将threshold调整到256kb,依次类推直到32mb.

glibc使用的两种用户程序内存管理机制,考虑了与系统底层通信代价,如果直接操纵大量小块内存,频繁地与系统调用进行通信,会降低程序的运行效率。将小块内存放入brk维护的堆中,等于实现了一 块缓存(cache),用完了可以先攒起来,到时候可以一起归还给系统。但是由于它的实现,只维护了堆顶的一个指针。因此想要内存归还系统,必须从顶向下,依次归还。在这种机制下,如果堆顶有块内存一直被占用,底下所有内存虽然都已经没用了,但是这种设计决定内存此时不可以还给系统,因此出现了“洞(Hole)”的问题。这种设计对一些由于业务需求,频繁申请/释放小块内存的用户程序而言,问题会比较突出。虽然glibc制定了这种有些“强硬”的内存管理方案,但是也提供了一些方法允许调节相关阈值(threshold),虽然不能干涉管理内存,但可以通过这些方法,决定“多大算大,多小算小”以及“攒到多少就归还”等这类问题。

3 mallopt() 与 malloc_trim(0)

1) mallopt是一个专门调节相关阈值的函数

URL: http://man7.org/linux/man-pages/man3/mallopt.3.html

#include < malloc.h >
int mallopt(int param, int value);M_MMAP_THRESHOLD For allocations greater than or equal to the limit specified (in  bytes) by M_MMAP_THRESHOLD that can't be satisfied from the free list,
the memory-allocation functions employ mmap(2) instead of increasing the  program break using sbrk(2).Allocating memory using mmap(2) has the significant advantage that  the allocated memory blocks can always be independently released back to  the system.
(By contrast, the heap can be trimmed only if memory is  freed at the top end.)
On the other hand, there are some disadvantages  to the use of mmap(2): deallocated space is not placed on the free list  for reuse by later allocations;
memory may be wasted because mmap(2)  allocations must be page-aligned; and the kernel must perform the  expensive task of zeroing out memory allocated
via mmap(2). Balancing  these factors leads to a default setting of 128*1024 for the  M_MMAP_THRESHOLD parameter. The lower limit for this parameter is 0.
The upper limit is DEFAULT_MMAP_THRESHOLD_MAX: 512x1024 on 32-bit systems or 4x1024x1024xsizeof(long) on 64-bit systems.Note: Nowadays, glibc uses a dynamic mmap threshold by default. The  initial value of the threshold is 128x1024, but when blocks larger than  the currentthreshold and less than or equal to  DEFAULT_MMAP_THRESHOLD_MAX are freed, the threshold is adjusted upwards  to the size of the freed block.When dynamic mmap thresholding is in  effect, the threshold for trimming the heap is also dynamically adjusted  to be twice the dynamic mmap threshold.Dynamic adjustment of the mmap  threshold is disabled if any of the M_TRIM_THRESHOLD, M_TOP_PAD,  M_MMAP_THRESHOLD, or M_MMAP_MAX parameters is set.

The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that parameter.

The following values can be specified for param:

M_CHECK_ACTION

Setting this parameter controls how glibc responds when various kinds of programming errors are detected (e.g., freeing the same pointer twice). The 3 least significant bits (2, 1, and 0) of the value assigned to this parameter determine the glibc behavior, as follows:

Bit 0 If this bit is set,

then print a one-line message on stderr that provides details about the error. The message starts with the string "*** glibc detected ***", followed by the program name, the name of the memory-allocation function in which the error was detected, a brief description of the error, and the memory address where the error was detected.

Bit 1 If this bit is set,

then, after printing any error message specified by bit 0, the program is terminated by calling abort(3). In glibc versions since 2.4, if bit 0 is also set, then, between printing the error message and aborting, the program also prints a stack trace in the manner of backtrace(3), and prints the process's memory mapping in the style of /proc/[pid]/maps (see proc(5)).

Bit 2 (since glibc 2.4),

This bit has an effect only if bit 0 is also set. If this bit is set, then the one-line message describing the error is simplified to contain just the name of the function where the error was detected and the brief description of the error.

The remaining bits in value are ignored.

Combining the above details, the following numeric values are meaningful for M_CHECK_ACTION:

0 Ignore error conditions; continue execution (with undefined results).
1 Print a detailed error message and continue execution.
2 Abort the program.
3 Print detailed error message, stack trace, and memorymappings, and abort the program.
5 Print a simple error message and continue execution.
7 Print simple error message, stack trace, and memory mappings, and abort the program.

Since glibc 2.3.4, the default value for the M_CHECK_ACTION parameter is 3. In glibc version 2.3.3 and earlier, the default value is 1. Using a nonzero M_CHECK_ACTION value can be useful because otherwise a crash may happen much later, and the true cause of the problem is then very hard to track down.

M_MMAP_MAX

This parameter specifies the maximum number of allocation requests that may be simultaneously serviced using mmap(2). This parameter exists because some systems have a limited number of internal tables for use by mmap(2), and using more than a few of them may degrade performance.

The default value is 65,536, a value which has no special significance and which servers only as a safeguard. Setting this parameter to 0 disables the use of mmap(2) for servicing large allocation requests.

M_MMAP_THRESHOLD

For allocations greater than or equal to the limit specified (in bytes) by M_MMAP_THRESHOLD that can't be satisfied from the free list, the memory-allocation functions employ mmap(2) instead of increasing the program break using sbrk(2).

Allocating memory using mmap(2) has the significant advantage that the allocated memory blocks can always be independently released back to the system. (By contrast, the heap can be trimmed only if memory is freed at the top end.) On the other hand, there are some disadvantages to the use of mmap(2): deallocated space is not placed on the free list for reuse by later allocations; memory may be wasted because mmap(2) allocations must be page-aligned; and the kernel must perform the expensive task of zeroing out memory allocated via mmap(2). Balancing these factors leads to a default setting of 128*1024 for the M_MMAP_THRESHOLD parameter. The lower limit for this parameter is 0. The upper limit is DEFAULT_MMAP_THRESHOLD_MAX: 512*1024 on 32-bit systems or 4*1024*1024*sizeof(long) on 64-bit systems. Note: Nowadays, glibc uses a dynamic mmap threshold by default. The initial value of the threshold is 128*1024, but when blocks larger than the current threshold and less than or equal to DEFAULT_MMAP_THRESHOLD_MAX are freed, the threshold is adjusted upward to the size of the freed block. When dynamic mmap thresholding is in effect, the threshold for trimming the heap is also dynamically adjusted to be twice the dynamic mmap threshold. Dynamic adjustment of the mmap threshold is disabled if any of the M_TRIM_THRESHOLD, M_TOP_PAD, M_MMAP_THRESHOLD, or M_MMAP_MAX parameters is set.

M_MXFAST (since glibc 2.3)

Set the upper limit for memory allocation requests that are satisfied using "fastbins". (The measurement unit for this parameter is bytes.) Fastbins are storage areas that hold deallocated blocks of memory of the same size without merging adjacent free blocks. Subsequent reallocation of blocks of the same size can be handled very quickly by allocating from the fastbin, although memory fragmentation and the overall memory footprint of the program can increase. The default value for this parameter is 64*sizeof(size_t)/4 (i.e., 64 on 32-bit architectures). The range for this parameter is 0 to 80*sizeof(size_t)/4. Setting M_MXFAST to 0 disables the use of fastbins.

M_PERTURB (since glibc 2.4)

If this parameter is set to a nonzero value, then bytes of allocated memory (other than allocations via calloc(3)) are initialized to the complement of the value in the least significant byte of value, and when allocated memory is released using free(3), the freed bytes are set to the least significant byte of value. This can be useful for detecting errors where programs incorrectly rely on allocated memory being initialized to zero, or reuse values in memory that has already been freed.

M_TOP_PAD

This parameter defines the amount of padding to employ when calling sbrk(2) to modify the program break. (The measurement unit for this parameter is bytes.) This parameter has an effect in the following circumstances:

  • When the program break is increased, then M_TOP_PAD bytes are added to the sbrk(2) request.
  • When the heap is trimmed as a consequence of calling free(3) (see the discussion of M_TRIM_THRESHOLD) this much free space is preserved at the top of the heap.

In either case, the amount of padding is always rounded to a system page boundary. Modifying M_TOP_PAD is a trade-off between increasing the number of system calls (when the parameter is set low) and wasting unused memory at the top of the heap (when the parameter is set high). The default value for this parameter is 128*1024.

M_TRIM_THRESHOLD

When the amount of contiguous free memory at the top of the heap grows sufficiently large, free(3) employs sbrk(2) to release this memory back to the system. (This can be useful in programs that continue to execute for a long period after freeing a significant amount of memory.) The M_TRIM_THRESHOLD parameter specifies the minimum size (in bytes) that this block of memory must reach before sbrk(2) is used to trim the heap.

The default value for this parameter is 128*1024. Setting M_TRIM_THRESHOLD to -1 disables trimming completely. Modifying M_TRIM_THRESHOLD is a trade-off between increasing the number of system calls (when the parameter is set low) and wasting unused memory at the top of the heap (when the parameter is set high).

Environment variables

A number of environment variables can be defined to modify some of the same parameters as are controlled by mallopt(). Using these variables has the advantage that the source code of the program need not be changed. To be effective, these variables must be defined before the first call to a memory-allocation function. (If the same parameters are adjusted via mallopt(), then the mallopt() settings take precedence.) For security reasons, these variables are ignored in set-user-ID and set-group-ID programs.

The environment variables are as follows (note the trailing underscore at the end of the name of each variable):

MALLOC_CHECK_

This environment variable controls the same parameter as mallopt() M_CHECK_ACTION. If this variable is set to a nonzero value, then a special implementation of the memory- allocation functions is used. (This is accomplished using the malloc_hook(3) feature.) This implementation performs additional error checking, but is slower than the standard set of memory-allocation functions. (This implementation does not detect all possible errors; memory leaks can still occur.)

The value assigned to this environment variable should be a single digit, whose meaning is as described for M_CHECK_ACTION. Any characters beyond the initial digit are ignored. For security reasons, the effect of MALLOC_CHECK_ is disabled by default for set-user-ID and set-group-ID programs. However, if the file /etc/suid-debug exists (the content of the file is irrelevant), then MALLOC_CHECK_ also has an effect for set-user-ID and set-group-ID programs.

MALLOC_MMAP_MAX_

Controls the same parameter as mallopt() M_MMAP_MAX.

MALLOC_MMAP_THRESHOLD_

Controls the same parameter as mallopt() M_MMAP_THRESHOLD.

MALLOC_PERTURB_

Controls the same parameter as mallopt() M_PERTURB.

MALLOC_TRIM_THRESHOLD_

Controls the same parameter as mallopt() M_TRIM_THRESHOLD.

MALLOC_TOP_PAD_

Controls the same parameter as mallopt() M_TOP_PAD.

2) malloc_trim()

负责告诉glibc在brk维护的堆队列中,堆顶留下多少的空余空间(free space),其他往上的空余空间全部归还给系统。它不能归还除堆顶之外的内存。

URL: http://man7.org/linux/man-pages/man3/malloc_trim.3.html

       #include <malloc.h>int malloc_trim(size_t  pad);

The malloc_trim () function attempts to release free memory at the top of the heap (by calling  sbrk(2) with a suitable argument).

The pad argument specifies the amount of free space to leave untrimmed at the top of the heap. If this argument is 0, only the minimum amount of memory is maintained at the top of the heap (i.e., one page or less). A nonzero argument can be used to maintain some trailing space at the top of the heap in order to allow future allocations to be made without having to extend the heap with  sbrk(2) .

4 解决方法

从操作系统的角度看,进程的内存分配由两个系统调用完成:brk和mmap。brk是将数据段(.data)的最高地址指针 _edata往高地址推,mmap是在进程的虚拟地址空间中找一块空闲的。其中,mmap分配的内存由munmap释放,内存释放时将立即归还操作系统; 而brk分配的内存需要等到高地址内存释放以后才能释放。 也就是说,如果先后通过brk申请了A和B两块内存,在B释放之前,A是不可能释放的,仍然被进程占用,通过TOP查看疑似”内存泄露”。默认情况下,大 于等于128KB的内存分配会调用mmap/mummap,小于128KB的内存请求调用sbrk(可以通过设置M_MMAP_THRESHOLD来调 整)。Glibc的:M_MMAP_THRESHOLD可以动态调整。M_MMAP_THRESHOLD的值在128KB到32MB(32位机)或者64MB(64位机)之间动态调整,每次申请并释放一个大小为2MB的内存后,M_MMAP_THRESHOLD的值被调整为2M到2M + 4K之间的一个值. 例如:

char* no_used = new char[2 * 1024 * 1024]; memset(no_used, 0xfe, 2 * 1024 * 1024); delete[] no_used;

这样,M_MMAP_THRESHOLD的值调整为2M到2M + 4K之间的一个值,后续申请 <= 2 * 1024 * 1024的内存块都会走sbrk而不是mmap,而sbrk需要等到高地址内存释放以后低地址内存才能释放。

可以显式设置M_MMAP_THRESHOLD或者 M_MMAP_MAX来关闭M_MMAP_THRESHOLD动态调整的特性,从而避免上述问题。

当然,mmap调用是会导致进程产生缺页中断的,为了提高性能,常见的做法如下:

1) 将动态内存改为静态,比如采用内存池技术或者启动的时候给每个线程分配一定大小,比如8MB的内存,以后直接使用;

2) 禁止mmap内存调用,禁止Glibc内存缩紧将内存归还系统,Glibc相当于实现了一个内存池功能。只需要在进程启动的时候加入两行代码:

mallopt(M_MMAP_MAX, 0); // 禁止malloc调用mmap分配内存

mallopt(M_TRIM_THRESHOLD, 0); // 禁止内存缩进,sbrk申请的内存释放后不会归还给操作系统

Linux下glibc内存管理相关推荐

  1. fork的黑科技,它到底做了个啥,源码级分析linux内核的内存管理

    最近一直在学习linux内核源码,总结一下 https://github.com/xiaozhang8tuo/linux-kernel-0.11 一份带注释的源码,学习用. fork的黑科技,它到底做 ...

  2. 深入研究glibc内存管理器原理及优缺点

    最近查清了线上内存占用过大和swap使用频繁的原因:由于linux使用的glibc使用内存池技术导致的堆外内存暴增,基于这个过程中学习和了解了glibc的内存管理原理,和大家分享,如有错误请及时指出. ...

  3. linux系统调用和内存管理以及栈帧

    linux编程和C++笔记 目录 linux编程和C++笔记 0. terminal操作快捷键等 1. vfork的子进程return程序会挂掉,但exit不会 2. C++内存管理详解 3. 关于堆 ...

  4. 2万字|30张图带你领略glibc内存管理精髓(因为OOM导致了上千万损失)

    由于此文涉及内容较多,且malloc和free的流程图太大,导致被压缩,需要本文pdf或者高清 原图的,请关注公众号[高性能架构探索],也可以后台回复[pdf],获取计算机必备经典书籍 前言 大家好, ...

  5. linux内核之内存管理.doc,linux内核之内存管理.doc

    Linux内核之内存管理 作者:harvey wang 邮箱:harvey.perfect@ 新浪博客地址:/harveyperfect ,有关于减肥和学习英语相关的博文,欢迎交流 把linux内存管 ...

  6. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  7. Linux内核在中国大发展的黄金十年-写于中国Linux存储、内存管理和文件系统峰会十周年之际...

    Linux阅码场: 国内首屈一指的专注Linux核心技术开发的公众号,扫描下方二维码关注 CLSF: CLSF是中国Linux存储.内存管理和文件系统峰会的简称, 至今已举办十年, 参会成员由组委会根 ...

  8. linux 内存大页,Linux大页内存管理等---菜鸟初学

    1. 查看linux的内存情况: free -m 2. 查看是否开启大页的方法: cat /proc/meminfo |grep -i HugePage AnonHugePages: 276480 k ...

  9. linux脚本查看系统内存,二个linux下查看内存使用情况的shell脚本()

    摘要 腾兴网为您分享:二个linux下查看内存使用情况的shell脚本(),政务易,悦作业,优化大师,王者荣耀等软件知识,以及单向历app,优路教育app,kimoji,开关电源设计软件,皮皮高清影视 ...

最新文章

  1. 【C语言入门教程】3.4 循环控制语句
  2. CTFshow php特性 web105
  3. 3.1.5 动态分区分配算法
  4. CentOS 编译安装 Nodejs (实测 笔记 Centos 7.3 + node 6.9.5)
  5. 存储服务器的操作系统,存储服务器是什么操作系统
  6. 【ArcGIS遇上Python】Python使用栅格数据
  7. html:(14):给div命名和table标签
  8. 在Web.Config中如何引入相对目录下的Access文件
  9. 安卓案例:View动画 - 弹球碰壁
  10. AIR如何实现窗口顶置
  11. 论文笔记_S2D.45_DeepVO: 基于深度循环卷积神经网络的端到端视觉里程计
  12. 华硕触控板无法在Win11中使用的解决办法
  13. JAVA----Quartz SimpleTrigger和CronTrigger 触发器
  14. Unity3D研究院之mac上从.ipa中提取unity3D游戏资源
  15. C++ 工厂模式 总结分析
  16. An Adaptive Stochastic Dominant Learning Swarm Optimizer for High-Dimensional Optimization 阅读笔记
  17. STM32F427/STM32F437高性能MCU微控制器介绍
  18. mysql数据库三表联查
  19. ndoutils2.2.0(ndo2db)中文乱码问题解决
  20. SQL注入原理与防御姿势(问答方式 描述)

热门文章

  1. C++ 发手机短信(很易很简单)
  2. 基于epoll实现一个IO多路复用的回声服务器
  3. 不需要任何依赖的图片加载错误处理的工具类load-image.js
  4. 深入剖析通信层和RPC调用的异步化(上)
  5. 真正的 Tornado 异步非阻塞
  6. 汇编程序:输数字,出字母
  7. Spring学习的切入点
  8. Codeforces Round #355 (Div. 2) B. Vanya and Food Processor 水题
  9. 从茶叶蛋到互联网思维
  10. Android属性动画源代码解析(超详细)