1.Glibc Malloc(PTMalloc) 简介
2.c库的malloc和free到底是如何实现的?
3.glibc malloc 原理简析
4.glibc 特性梳理 | 使用 hugetlb 提升性能
5.一次 Java 进程 OOM 的排查分析(glibc 篇)
6.*** glibc detected *** ./test: free(): invalid next size (fast): 0x084bb060 *** å¨linuxä¸C读åxml
Glibc Malloc(PTMalloc) 简介
Glibc Malloc,即PTMalloc,是Linux操作系统中广泛使用的内存分配器。本文主要阐述其核心机制和工作流程。
PTMalloc的历史可以追溯到早期的ptmalloc,而glibc的java开放源码malloc则是在此基础上演化的。其设计旨在满足多线程环境下的高效内存管理需求。
在PTMalloc中,内存被划分为不同大小的多个chunk。每个chunk包含一个metadata,用于指示其大小以及与相邻chunk的关联信息。当程序请求内存时,实际操作的只是chunk的大小信息。当释放内存时,被程序占用的部分将被标记为可再分配,并重新用于存储与arena相关的额外信息。每个chunk的最后一个word存储了其大小信息的副本,其3个最低有效位用于标志位。在chunk指针(mchunkptr)中,指向的并非是chunk的开始位置,而是前一个chunk的最后一个word。
PTMalloc中引入了Arena和Heap的概念。为了支持多线程并发,系统允许创建多个同时活跃的内存区域,每个区域称为一个arena。main arena作为程序启动时的初始heap,而额外的arena则通过mmap方式动态分配。arena的数量最多是CPU核心数的8倍,以减少竞争并降低内存碎片。
每个arena拥有一个互斥锁(mutex)以控制对内存区域的访问。某些操作,如访问fastbin,使用原子操作,无需锁住arena。其它操作需要线程对arena加锁。ios室内导航源码多个arena的设置旨在减少线程间的竞争,通过将线程分配到不同arena,避免了等待其他线程释放资源的情况。
arena从heap获取内存。main arena使用程序的初始heap(从.bss段开始),额外arena通过mmap分配额外heap空间。每个arena跟踪一个特殊的top chunk,通常是最可用的chunk,也是最近分配的heap。分配给arena的内存通常来自其初始heap。
为了快速找到合适的chunk满足分配请求,PTMalloc将空闲chunk根据大小和历史信息存储在不同的列表中,即所谓的“bin”。这些bin分为多种类型,包括large chunk的额外双向链表,用于高效地查找适合的chunk。
每个线程拥有自己的thread-local变量,用于记录上次使用的arena。当线程需要使用arena时,若arena被占用,线程将被阻塞直至可用。若线程从未使用过arena,将尝试创建新arena、使用未使用的arena或从全局列表中选取下一个arena。
每个线程还拥有一个per-thread缓存(tcache),包含用于快速访问的小集合的chunk。tcache使用单链表存储,类似于fastbin,但链接到载荷而非chunk header。每个bin对应特定大小的chunk,数组通过chunk size间接寻址。与fastbin不同的是,tcache对每个bin的chunk数量有限制,若请求的ss-panel 源码大小在缓存中未找到,将回退到传统的malloc方法,即对arena加锁。
简而言之,malloc算法涉及查找合适的chunk以满足分配请求。对于大对齐的malloc,如valloc、pvalloc、memalign,系统会找到合适的chunk,然后将大chunk分割成多个适合对齐的chunk,剩余部分会被放置在unsorted列表以供重复使用。
free操作标记内存中的chunk为可再分配,但实际内存并未返回给操作系统。只有在top chunk变为足够大时,部分内存才会被unmmap并返回给OS。
realloc操作则基于内存分配和释放的机制,根据新分配的大小调整已分配内存,确保资源的高效使用。
对于由mmap创建的chunk,其分配和释放遵循特定的机制,包括使用mremap重新分配内存,这可能导致与原始内存地址不同。对于arena chunk,realloc操作涉及调整已分配内存的大小,以适应新的需求。在某些情况下,当OS不支持munmap()时,对于新大小小于旧大小的realloc,内存不会发生任何变化,仍返回原始地址。然而,如果新大小小于旧大小并支持munmap(),则会执行malloc-copy-free流程。
在进程生命周期中,swf反编译 源码线程绑定的arena通常保持不变。但在内存资源紧张时,线程可能需要在sbrk的main arena与通过mmap创建的非main arena之间切换,以寻找满足分配需求的heap。
c库的malloc和free到底是如何实现的?
在使用C语言时,对内存管理的了解是至关重要的。其中,glibc库中的malloc和free函数是内存管理的核心。过去,许多人误以为malloc和free仅仅是glibc与操作系统间的桥梁,应用程序直接通过这些函数申请和释放内存。然而,深入分析glibc源码后,我们发现malloc和free的实现远比表面复杂。在实际应用中,malloc和free的操作实际上是在一个称为内存池(我们暂称为ptmalloc)的内部进行的。
当应用程序调用malloc时,实际上是在ptmalloc中申请内存。ptmalloc内部维护了多个内存池,包括fast bins、small bins、largebins、top chunk、mmaped chunk以及lastremainder chunk。内存的分配和释放操作主要在这几个内存池中进行。只有满足特定条件时,ptmalloc才会调用sys_trim函数,将不再使用的内存块归还给操作系统。
接下来,让我们简要概述一下malloc和free的实现流程。在申请内存时,malloc首先查找合适的内存池,找到空闲内存块后分配给应用程序。释放内存时,车辆进出统计源码free将内存块放回相应的内存池,等待ptmalloc进一步的分配。整个过程中,glibc内部的内存管理机制负责内存的高效管理和回收。
了解malloc和free的内部实现,对优化程序性能和防止内存泄漏至关重要。通过深入研究glibc的内存管理机制,我们可以更好地控制内存使用,提高程序的稳定性和效率。
glibc malloc 原理简析
在软件开发的舞台中,内存管理是基石之一。腾讯工程师abush在其最新文章中揭示了TencentOS Server 4的glibc升级到2.版本中的关键亮点——glibc malloc的强大内核,特别是ptmalloc的革新tcache机制。glibc,作为开源C标准库,以其卓越的内存分配和管理能力闻名,其核心任务是动态内存的高效分配与回收。
glibc的内存管理策略犹如精密的钟表,核心数据结构包括chunk和arena。chunk,作为最小的内存单元,它携带着prev_size、mchunk_size、fd和bk等字段,它们如同内存的指针,揭示了chunk的状态。arena则是内存分配的舞台,分为主线程分配区和线程私有区域,通过链表巧妙地管理不同大小的chunk,构建出灵活的内存分配格局。
malloc的世界里,tcache、fastbin、unsortedbin、smallbin和largebin各司其职。tcache,如同内存分配的快速通道,特别针对小内存请求提供了显著的性能提升,每个线程都有自己的专属tcache。fastbin则负责管理那些小块内存,unsortedbin则收纳那些整合后的未排序chunk,而smallbin和largebin则根据特定规则有序地管理chunk。malloc通过精心设计的缓存策略和工作流程,优先考虑tcache,继而fastbin,再到unsortedbin,最后是smallbin和largebin,形成了一套高效的内存分配流程。然而,释放内存的顺序却与之相反,以保持内存的连续性。
参数调优是glibc malloc的另一大亮点。通过环境变量,如M_MMAP_MAX、M_MMAP_THRESHOLD和M_TOP_PAD,开发者可以调整内存分配策略以适应不同场景。调整方法如:
GLIBC_TUNABLES=glibc.malloc.mmap_max=1:glibc.malloc.top_pad=1
查看支持的参数列表,只需运行:
/lib/ld-linux-x-.so.2 --list-tunables
对于开发者来说,glibc的malloc_stats函数是不可或缺的工具,它提供了一窥内存使用状况的窗口:
#include <stdlib.h>
#include <malloc.h>
void malloc_stats();
在实际应用中,一个典型的输出示例如下:
Arena 0: system bytes = , in use bytes =
想要了解更多技术前沿和实践案例,不妨关注鹅厂架构师公众号,那里有丰富的技术资讯和深度解析,助你深入理解内存管理的奥秘。
glibc 特性梳理 | 使用 hugetlb 提升性能
以下是腾讯工程师 abush 的文章内容梳理,详细介绍了 glibc 特性中的 hugetlb 特性及其如何提升性能。在 2. 版本的社区中,为 malloc 引入了 hugetlb 特性,允许用户根据需求申请大页以提升特定场景的性能。背景介绍Hugepages 机制
Hugepages 是 Linux 内核提供的一种内存分配机制,旨在通过以更大的页面大小分配内存,优化内存访问效率,尤其适用于需要大量连续内存空间的应用,如数据库、虚拟机及高性能计算任务。与系统级设置不同,glibc 的 hugetlb 参数以进程为单位调整 malloc 的行为,根据用户期望的方式从大页申请内存,既提升性能,又不干扰系统全局设置。
使用方式
透明大页
设置方式
确认透明大页状态、设置 glibchugetlb 环境变量,并通过简单的 malloc & memset 用例测试性能提升。通过命令检查系统支持的默认大页大小,设置 nr_hugepages 后,使用 glibchugetlb 环境变量,然后验证内存分配效果。
普通大页
当 hugetlb 参数值大于 1 时,glibc 支持两种使用场景:一是使用系统默认的大页,二是允许用户自定义大页大小。系统默认大页大小可通过命令确认,设置 nr_hugepages 后,预留相应数量的大页,确保内存分配性能提升。自定义大页同样预留足够数量的大页,通过 glibchugetlb 环境变量指定大小,并验证内存分配效果。
原理介绍
在首次 malloc 时,glibc 解析 glibchugetlb 环境变量,根据用户指定的值确定大页大小。不同值对应不同的处理逻辑,包括读取透明大页大小、系统默认大页大小或用户指定的大页大小。通过将大页大小传递给 mmap,实现使用透明大页。此外,glibc 对于透明大页的使用,通过在每次 mmap 后调用 madvise_thp 实现。
总结
大页机制通过优化内存访问效率提升性能,但也需合理规划,避免内存浪费。glibc 的 hugetlb 特性通过提供更加灵活的内存管理方式,允许用户根据需求申请大页,同时减少内存浪费。TencentOS Server 4 的 glibc 版本已集成这一特性,对有相关需求的用户来说是一个值得体验的选择。
一次 Java 进程 OOM 的排查分析(glibc 篇)
一次 Java 进程因 glibc 导致的内存问题引发的排查分析
近期,遇到一个 Java RPC 项目在容器内存达到 1.5G 限制后被自动终止,问题聚焦于 glibc 内存管理。在本地测试中,尽管堆内存仅占 M,非堆内存也只有 M,但进程 RES 内存远超预期。
通过 arthas 查看内存分布,怀疑堆外内存和 native 内存可能存在泄露,但启用 NMT 未见明显增长。接着,注意到 Linux M 内存问题,pmap 显示大量 M 内存区域,指向 glibc 的 ptmalloc2 与 arena 结构。ptmalloc2 通过增加多个 arena 以缓解多线程锁竞争,但M内存区域数量过多引发疑问。
尝试设置 MALLOC_ARENA_MAX=1,内存区域减少,但集中在较大的区域,暗示 arena 数量并非问题关键。进一步,通过自定义 malloc hook,追踪到线程 在大量处理 jar 包解压,但即使强制 GC 也未见内存下降,表明内存可能并未正确释放。
深入分析揭示,即使顶层调用未释放内存,Inflater 的 finalize 方法会处理,但内存并未释放,归咎于 glibc 自身问题,它未能将内存归还给系统。理解 glibc 内存分配原理和 chunk 结构至关重要,这涉及到 Arena、chunk 和 fastbin 等概念。
实验显示内存碎片影响 glibc 回收,malloc_trim 虽然初衷是归还堆顶内存,但实测效果超出预期。jemalloc 的引入显著改善了内存占用,证明了不同内存管理库在碎片处理上的差异。然而,malloc_trim 使用需谨慎,可能引发 JVM Crash。
最后,内存问题的排查和解决方案往往涉及复杂的设计权衡,写 malloc 库的实践也加深了对内存管理复杂性的理解。未来将继续深入讨论内存分配和管理的细节。
*** glibc detected *** ./test: free(): invalid next size (fast): 0xbb *** å¨linuxä¸C读åxml
pFileName = (char *)malloc(sizeof(char));
å ååé 太å°äºåªæä¸ä¸ªåèï¼è¿è¡ä¸é¢çå¥åå¿ ç¶è¸©å åï¼
sprintf(pFileName, "%s/bin/new.xml",getenv("HOME"));
å¯æ¹ä¸º
pFileName = (char *)malloc(sizeof(char)*);
musl和glibc,性能区别到底有多大?
在探索musl和glibc性能差异时,发现musl在某些函数实现上可能较慢,如malloc系列和memcpy系列函数。特别在多线程环境下,musl的malloc性能会显著影响效率,原因在于每次malloc时都需要全局变量加锁解锁,造成严重竞争现象。
然而,musl的源代码简洁,易于管理,相较于glibc的复杂代码结构,替换性能较慢的函数能带来显著性能提升。在使用Gentoo Linux系统并采用LLVM clang/lld/libc++/libc++abi/libunwind时,通过替换关键函数,编译速度优于使用glibc的系统。
对于不希望修改musl源码的情况,可直接链接高性能malloc实现,例如微软的github.com/microsoft/mi... 或者是GitHub - mjansson/rpmalloc: Public domain cross platform lock free thread caching -byte aligned memory allocator implemented in C。mimalloc目前被认为是性能最高的开源malloc实现,使用安全模式版本在很多情况下比大部分malloc更快。rpmalloc性能也很接近,且代码精简。
虽然musl的qsort实现不是最快的,但rust标准库使用的pdqsort是最快算法,不过在C中正确实现pdqsort较为复杂,因此未进行替换。毕竟glibc的qsort性能也非最优。
建议使用musl时,一并采用LLVM libc++,因为Apple和Google的两大企业支持,性能相较于libstdc++有明显提升。