`
sunqi
  • 浏览: 230126 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

频繁分配释放内存导致的性能问题的分析(转载)

阅读更多

现象
1 压力测试过程中,发现被测对象性能不够理想,具体表现为:
进程的系统态CPU消耗20,用户态CPU消耗10,系统idle大约70
2 用ps -o majflt,minflt -C program命令查看,发现majflt每秒增量为0,而minflt每秒增量大于10000。

初步分析
majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误。
这两个数值表示一个进程自启动以来所发生的缺页中断的次数。
当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:
检查要访问的虚拟地址是否合法
查找/分配一个物理页
填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)
建立映射关系(虚拟地址到物理地址)
重新执行发生缺页中断的那条指令
如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。
此进程minflt如此之高,一秒10000多次,不得不怀疑它跟进程内核态cpu消耗大有很大关系。

分析代码
查看代码,发现是这么写的:一个请求来,用malloc分配2M内存,请求结束后free这块内存。看日志,发现分配内存语句耗时10us,平均一条请求处理耗时1000us 。 原因已找到!
虽然分配内存语句的耗时在一条处理请求中耗时比重不大,但是这条语句严重影响了性能。要解释清楚原因,需要先了解一下内存分配的原理。

内存分配的原理
从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。brk是将数据段(.data)的最高地址指针_edata往高地址推,mmap是在进程的虚拟地址空间中(一般是堆和栈中间)找一块空闲的。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。
下面以一个例子来说明内存分配的原理:

 

1进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。_edata指针(glibc里面定义)指向数据段的最高地址。
2进程调用A=malloc(30K)以后,内存空间如图2:malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。你可能会问:只要把_edata+30K就完成内存分配了?事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。
3进程调用B=malloc(40K)以后,内存空间如图3.
1进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。_edata指针(glibc里面定义)指向数据段的最高地址。
2进程调用A=malloc(30K)以后,内存空间如图2:malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。你可能会问:只要把_edata+30K就完成内存分配了?事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。
3进程调用B=malloc(40K)以后,内存空间如图3.
7进程调用free(B)以后,如图7所示。B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢?当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了。
8进程调用free(D)以后,如图8所示。B和D连接起来,变成一块140K的空闲内存。
9默认情况下:当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。

真相大白
说完内存分配的原理,那么被测模块在内核态cpu消耗高的原因就很清楚了:每次请求来都malloc一块2M的内存,默认情况下,malloc调用mmap分配内存,请求结束的时候,调用munmap释放内存。假设每个请求需要6个物理页,那么每个请求就会产生6个缺页中断,在2000的压力下,每秒就产生了10000多次缺页中断,这些缺页中断不需要读取磁盘解决,所以叫做minflt;缺页中断在内核态执行,因此进程的内核态cpu消耗很大。缺页中断分散在整个请求的处理过程中,所以表现为分配语句耗时(10us)相对于整条请求的处理时间(1000us)比重很小。

解决办法
将动态内存改为静态分配,或者启动的时候,用malloc为每个线程分配,然后保存在threaddata里面。但是,由于这个模块的特殊性,静态分配,或者启动时候分配都不可行。另外,Linux下默认栈的大小限制是10M,如果在栈上分配几M的内存,有风险。
禁止malloc调用mmap分配内存,禁止内存紧缩。
在进程启动时候,加入以下两行代码:
mallopt(M_MMAP_MAX, 0); // 禁止malloc调用mmap分配内存
mallopt(M_TRIM_THRESHOLD, -1); // 禁止内存紧缩
效果:加入这两行代码以后,用ps命令观察,压力稳定以后,majlt和minflt都为0。进程的系统态cpu从20降到10。

小结
可以用命令ps -o majflt minflt -C program来查看进程的majflt, minflt的值,这两个值都是累加值,从进程启动开始累加。在对高性能要求的程序做压力测试的时候,我们可以多关注一下这两个值。
如果一个进程使用了mmap将很大的数据文件映射到进程的虚拟地址空间,我们需要重点关注majflt的值,因为相比minflt,majflt对于性能的损害是致命的,随机读一次磁盘的耗时数量级在几个毫秒,而minflt只有在大量的时候才会对性能产生影响。

  • 大小: 41.7 KB
  • 大小: 46.4 KB
  • 大小: 41.6 KB
分享到:
评论

相关推荐

    易语言分配可用内存空间

    例如,如果知道内存的使用规模,可以一次性分配足够的内存,减少频繁的内存申请和释放操作。 在提供的源码"易语言分配可用内存空间"中,应该详细展示了如何在易语言环境中进行内存的申请、使用和释放,通过学习和...

    自定义内存池性能优化的原理

    这些开销在频繁的内存分配和释放操作中尤为明显,可能导致性能下降,并且容易产生内存碎片问题,降低内存利用率。 ##### 2.2 内存池的定义和分类 自定义内存池通过预先申请一块较大的内存区域(即内存池),并在其...

    内存释放2.0.rar

    "内存释放2.0.rar"是一个专为Windows设计的内存优化工具,旨在帮助用户解决因内存占用过高导致的系统卡顿问题。内存释放工具通常通过释放不再使用的进程、缓存和内存页面,来提高系统响应速度和整体性能。 内存释放...

    智能释放内存管理器 释放无用的内存

    1. **内存分析**:首先,该工具会扫描整个系统的内存状态,识别哪些进程或应用程序在当前状态下不再活跃,或者它们所占用的内存可以被安全地回收。 2. **内存碎片整理**:在长时间运行后,内存中可能会形成大量的...

    嵌入式Linux内存与性能详解-史子旺

    接着,性能优化方面,可能会讲解如何使用内存池技术来预先分配内存,避免频繁的动态分配导致的性能瓶颈。此外,内存碎片问题及其解决方法,如内存对齐、紧凑等也是讨论的重点。书中可能还会介绍如何使用工具如`free`...

    内存释放工具2个

    1. **内存分析**:首先,工具会对系统的内存使用情况进行深入分析,检测哪些进程或应用程序占用了大量内存,以便找出可能的问题源头。 2. **内存释放**:通过终止占用内存的进程或者强制释放内存分配,工具可以直接...

    C内存分配算法之一最优先分配

    在C编程语言中,内存管理是一项关键任务,它涉及到如何有效地分配、使用和...最优先分配算法尽管不是最快速的分配方法,但它在优化内存利用率和减少碎片方面表现出色,尤其适用于需要频繁分配和释放小内存块的场景。

    释放内存小工具

    当内存使用率过高,可能会导致系统变慢或卡顿,因为计算机需要在内存和硬盘之间频繁交换数据,这被称为“页面交换”。释放内存小工具就是为了缓解这种情况而设计的。 这个小工具的英文界面虽然可能对一些非技术用户...

    分配内存单元

    9. **内存池技术**:为了解决频繁小内存块分配和释放导致的碎片问题,引入了内存池的概念,预先分配一大块内存,然后从中分配和回收小块内存,减少系统调用,提高效率。 10. **内存安全**:确保程序在分配和使用...

    自动释放sql server 进程内存的程序,附源代码

    1. 在释放内存前,确保了解SQL Server的当前内存状态,避免在高负载时进行释放操作,这可能导致查询性能下降。 2. 频繁的内存释放可能会增加SQL Server的工作负担,因此要合理设置释放间隔和条件,平衡性能和资源...

    内存释放专家内存释放专家.zip

    2. **内存分析**:一旦发现可疑的内存泄漏,内存释放专家将进行深入分析,确定这些内存块是否真的不再被程序使用。 3. **安全释放**:在确认内存块可以安全释放后,内存释放专家会执行释放操作,而不会影响到程序的...

    3dMax一键释放内存工具

    然而,值得注意的是,虽然这个插件可以有效缓解内存问题,但过度依赖内存释放可能导致数据丢失或软件不稳定。因此,建议用户在关键时刻保存工作,并在适当的时候使用此工具。同时,合理规划和优化3DMax项目,如降低...

    内存释放专家

    对于开发者而言,理解内存管理的基本原理,熟练使用各种内存分析工具,可以有效地预防和解决内存相关的问题。 总的来说,"内存释放专家"是一款旨在提高系统效率、防止内存泄漏和优化内存使用的实用工具,它能够帮助...

    内存智能释放超级好用

    - **内存预分配与延迟释放**:预分配内存可以减少程序运行时的内存碎片,延迟释放则可以避免频繁释放导致的系统开销。 5. **实际应用中的智能内存释放** - **编程语言支持**:不同的编程语言有不同的内存管理机制...

    动态内存分配算法(源代码&报告)DynamicAllocate

    动态内存分配的主要优点是灵活性,它使得程序员能够控制内存的使用,避免了固定大小数组可能导致的问题,如溢出或浪费空间。 在动态内存分配中,主要有以下几种常见的算法: 1. **首次适配算法 (First Fit)**: 这...

    内存释放专家很好用的

    过度依赖内存释放工具可能会干扰这些机制,反而导致性能问题。因此,正确使用内存释放工具,配合良好的软件开发实践,才能真正解决内存管理问题。 在实际使用中,用户应当注意以下几点: - 不要无脑依赖内存释放...

    内存整理工具 释放内存 清理内存 超级兔子内存工具

    “超级兔子内存工具”作为一款专业内存管理软件,其工作原理可能包括实时监控内存使用情况,分析内存分配,找出可释放的内存块,并进行有效的整理。此外,它可能还具备一些附加功能,如系统垃圾清理、注册表修复等,...

    计算机操作系统内存分配实验报告.doc

    首次适应算法以空闲分区的起始地址为依据,优先选择找到的第一个足够大的空闲分区进行分配,以避免频繁地在内存低地址部分分配导致碎片。 最佳适应算法同样按照空闲分区的起始地址排序,但选择最小的能满足作业需求...

Global site tag (gtag.js) - Google Analytics