<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; 堆层</title>
	<atom:link href="https://www.phppan.com/tag/%e5%a0%86%e5%b1%82/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 30 May 2026 09:35:05 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>PHP源码阅读笔记三十一：PHP内存池中的堆(heap)层基础</title>
		<link>https://www.phppan.com/2010/11/php-source-code-31-memory-pool-heap/</link>
		<comments>https://www.phppan.com/2010/11/php-source-code-31-memory-pool-heap/#comments</comments>
		<pubDate>Sat, 20 Nov 2010 10:28:32 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内存池]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[堆层]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1123</guid>
		<description><![CDATA[PHP源码阅读笔记三十一：PHP内存池中的堆(heap)层基础 【概述】 PHP的内存管理器是分层（hiera [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP源码阅读笔记三十一：PHP内存池中的堆(heap)层基础</p>
<p><strong>【概述】</strong><br />
PHP的内存管理器是分层（hierarchical）的。这个管理器共有三层：存储层（storage）、堆（heap）层和 emalloc/efree 层。在<a href="http://www.phppan.com/2010/11/php-source-code-30-memory-pool-storage/">PHP源码阅读笔记三十：PHP内存池中的存储层</a>中介绍了存储层，存储层通过 malloc()、mmap() 等函数向系统真正的申请内存，并通过 free() 函数释放所申请的内存。存储层通常申请的内存块都比较大，这里申请的内存大并不是指storage层结构所需要的内存大，只是堆层通过调用存储层的分配方法时，其以段的格式申请的内存比较大，存储层的作用是将内存分配的方式对堆层透明化。<br />
在存储层之上就是今天我们要了解的堆层。堆层一个调度层，它与上面的emalloc/efree层交互，将通过存储层申请到的大块内存，进行拆分，按需提供。在堆层中有其一套内存的调度策略，这个整个PHP内存分配管理的核心区域。</p>
<p>以下的所有分享都是基于ZEND_DEBUG未打开的情况。<br />
首先看下堆层所涉及到的结构：<br />
<strong>【结构】</strong></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
</pre></td><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #808080; font-style: italic;">/* mm block type */</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _zend_mm_block_info <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">size_t</span> _size<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* block的大小*/</span>
	<span style="color: #993333;">size_t</span> _prev<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 计算前一个块有用到*/</span>
<span style="color: #009900;">&#125;</span> zend_mm_block_info<span style="color: #339933;">;</span>
&nbsp;
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _zend_mm_block <span style="color: #009900;">&#123;</span>
	zend_mm_block_info info<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> zend_mm_block<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _zend_mm_small_free_block <span style="color: #009900;">&#123;</span>	<span style="color: #808080; font-style: italic;">/* 双向链表 */</span>
	zend_mm_block_info info<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">*</span>prev_free_block<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 前一个块 */</span>
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">*</span>next_free_block<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 后一个块 */</span>
<span style="color: #009900;">&#125;</span> zend_mm_small_free_block<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 小的空闲块*/</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #009900;">&#123;</span>	<span style="color: #808080; font-style: italic;">/* 双向链表 + 树结构 */</span>
	zend_mm_block_info info<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">*</span>prev_free_block<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 前一个块 */</span>
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">*</span>next_free_block<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 后一个块 */</span>
&nbsp;
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">**</span>parent<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 父结点 */</span>
	<span style="color: #993333;">struct</span> _zend_mm_free_block <span style="color: #339933;">*</span>child<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 两个子结点*/</span>
<span style="color: #009900;">&#125;</span> zend_mm_free_block<span style="color: #339933;">;</span>
&nbsp;
&nbsp;
&nbsp;
<span style="color: #993333;">struct</span> _zend_mm_heap <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span>                 use_zend_alloc<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 是否使用zend内存管理器 */</span>
	<span style="color: #993333;">void</span>               <span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>_malloc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">size_t</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 内存分配函数*/</span>
	<span style="color: #993333;">void</span>                <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>_free<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 内存释放函数*/</span>
	<span style="color: #993333;">void</span>               <span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>_realloc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*,</span> <span style="color: #993333;">size_t</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">size_t</span>              free_bitmap<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 小块空闲内存标识 */</span>
	<span style="color: #993333;">size_t</span>              large_free_bitmap<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* 大块空闲内存标识*/</span>
	<span style="color: #993333;">size_t</span>              block_size<span style="color: #339933;">;</span>		<span style="color: #808080; font-style: italic;">/* 一次内存分配的段大小，即ZEND_MM_SEG_SIZE指定的大小，默认为ZEND_MM_SEG_SIZE   (256 * 1024)*/</span>
	<span style="color: #993333;">size_t</span>              compact_size<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 压缩操作边界值，为ZEND_MM_COMPACT指定大小，默认为 2 * 1024 * 1024*/</span>
	zend_mm_segment    <span style="color: #339933;">*</span>segments_list<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 段指针列表 */</span>
	zend_mm_storage    <span style="color: #339933;">*</span>storage<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 所调用的存储层 */</span>
	<span style="color: #993333;">size_t</span>              real_size<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 堆的真实大小 */</span>
	<span style="color: #993333;">size_t</span>              real_peak<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 堆真实大小的峰值 */</span>
	<span style="color: #993333;">size_t</span>              limit<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 堆的内存边界 */</span>
	<span style="color: #993333;">size_t</span>              size<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 堆大小 */</span>
	<span style="color: #993333;">size_t</span>              peak<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 堆大小的峰值*/</span>
	<span style="color: #993333;">size_t</span>              reserve_size<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 备用堆大小*/</span>
	<span style="color: #993333;">void</span>               <span style="color: #339933;">*</span>reserve<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 备用堆 */</span>
	<span style="color: #993333;">int</span>                 overflow<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 内存溢出数*/</span>
	<span style="color: #993333;">int</span>                 internal<span style="color: #339933;">;</span>
<span style="color: #339933;">#if ZEND_MM_CACHE</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span>        cached<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 已缓存大小 */</span>
	zend_mm_free_block <span style="color: #339933;">*</span>cache<span style="color: #009900;">&#91;</span>ZEND_MM_NUM_BUCKETS<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 缓存数组/
#endif
	zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];	/* 小块空闲内存数组 */</span>
	zend_mm_free_block <span style="color: #339933;">*</span>large_free_buckets<span style="color: #009900;">&#91;</span>ZEND_MM_NUM_BUCKETS<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 大块空闲内存数组*/</span>
	zend_mm_free_block <span style="color: #339933;">*</span>rest_buckets<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 剩余内存数组 */</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>对于heap结构中的内存操作函数，如果use_zend_alloc为否，则使用malloc-type 内存分配，此时所有的操作就不经过堆层中的内存管理，直接采用malloc等函数。</p>
<p>compact_size的大小默认为 2 * 1024 * 1024（2M）,如果有设置变量ZEND_MM_COMPACT则为此指定大小，如果内存的峰值超过这个值，则会调用storage的compact函数，只是这个函数现在的实现为空，可能在后续的版本中添加。</p>
<p>reserve_size为备用堆的大小，默认情况下为ZEND_MM_RESERVE_SIZE，其大小为(8*1024)<br />
*reserve为备用堆，其大小为reserve_size，其用作内存溢出时报告错误用。</p>
<p><strong>【关于USE_ZEND_ALLOC】</strong><br />
环境变量 USE_ZEND_ALLOC 可用于允许在运行时选择 malloc 或 emalloc 内存分配。使用 malloc-type 内存分配将允许外部调试器观察内存使用情况，而 emalloc 分配将使用 Zend 内存管理器抽象，要求进行内部调试。<br />
[zend_startup() -> start_memory_manager() -> alloc_globals_ctor()]</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> alloc_globals_ctor<span style="color: #009900;">&#40;</span>zend_alloc_globals <span style="color: #339933;">*</span>alloc_globals TSRMLS_DC<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>tmp<span style="color: #339933;">;</span>
	alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap <span style="color: #339933;">=</span> zend_mm_startup<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	tmp <span style="color: #339933;">=</span> <span style="color: #000066;">getenv</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;USE_ZEND_ALLOC&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tmp<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap<span style="color: #339933;">-&gt;</span>use_zend_alloc <span style="color: #339933;">=</span> zend_atoi<span style="color: #009900;">&#40;</span>tmp<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap<span style="color: #339933;">-&gt;</span>use_zend_alloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>	<span style="color: #808080; font-style: italic;">/* 如果不使用zend的内存管理器，同直接使用malloc函数*/</span>
			alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap<span style="color: #339933;">-&gt;</span>_malloc <span style="color: #339933;">=</span> <span style="color: #000066;">malloc</span><span style="color: #339933;">;</span>
			alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap<span style="color: #339933;">-&gt;</span>_free <span style="color: #339933;">=</span> <span style="color: #000066;">free</span><span style="color: #339933;">;</span>
			alloc_globals<span style="color: #339933;">-&gt;</span>mm_heap<span style="color: #339933;">-&gt;</span>_realloc <span style="color: #339933;">=</span> <span style="color: #000066;">realloc</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p><strong>【初始化】</strong><br />
[zend_mm_startup()]<br />
初始化storage层的分配方案，初始化段大小，压缩边界值，并调用zend_mm_startup_ex()初始化堆层。</p>
<p>[zend_mm_startup() -> zend_mm_startup_ex()]<br />
<strong>【内存对齐】</strong><br />
在PHP的内存分配中使用了内存对齐，内存对齐计算显然有两个目标：一是减少CPU的访存次数；第二个就是还要保持存储空间的效率足够高。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;"># define ZEND_MM_ALIGNMENT 8</span>
&nbsp;
<span style="color: #339933;">#define ZEND_MM_ALIGNMENT_MASK ~(ZEND_MM_ALIGNMENT-1)</span>
&nbsp;
<span style="color: #339933;">#define ZEND_MM_ALIGNED_SIZE(size)	(((size) + ZEND_MM_ALIGNMENT - 1) &amp; ZEND_MM_ALIGNMENT_MASK)</span>
&nbsp;
<span style="color: #339933;">#define ZEND_MM_ALIGNED_HEADER_SIZE			ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block))</span>
<span style="color: #339933;">#define ZEND_MM_ALIGNED_FREE_HEADER_SIZE	ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block))</span></pre></td></tr></table></div>

<p>PHP在分配块的内存中，用到内存对齐，如果所需要的内存的大小的低三位不为0（不能为8整除），则将低三位加上7，并~7进行与操作，即对于大小不是8的整数倍的内存大小补全到可以被8整除。<br />
在win32机器上，一些宏对应的数值大小为：<br />
ZEND_MM_MIN_SIZE=8<br />
ZEND_MM_MAX_SMALL_SIZE=272<br />
ZEND_MM_ALIGNED_HEADER_SIZE=8<br />
ZEND_MM_ALIGNED_FREE_HEADER_SIZE=16<br />
ZEND_MM_MIN_ALLOC_BLOCK_SIZE=8<br />
ZEND_MM_ALIGNED_MIN_HEADER_SIZE=16<br />
ZEND_MM_ALIGNED_SEGMENT_SIZE=8</p>
<p>如果要分配一个大小为9个字节的块，则其实际分配的大小为ZEND_MM_ALIGNED_SIZE(9 + 8)=24</p>
<p><strong>【块的定位】</strong><br />
所分配的内存的右边的两位是用来标记内存的类型。<br />
其大小的定义为#define ZEND_MM_TYPE_MASK		ZEND_MM_LONG_CONST(0&#215;3)</p>
<p>如下所示代码为块的定位</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define ZEND_MM_NEXT_BLOCK(b)			ZEND_MM_BLOCK_AT(b, ZEND_MM_BLOCK_SIZE(b))</span>
<span style="color: #339933;">#define ZEND_MM_PREV_BLOCK(b)			ZEND_MM_BLOCK_AT(b, -(int)((b)-&gt;info._prev &amp; ~ZEND_MM_TYPE_MASK))</span>
&nbsp;
<span style="color: #339933;">#define ZEND_MM_BLOCK_AT(blk, offset)	((zend_mm_block *) (((char *) (blk))+(offset)))</span>
<span style="color: #339933;">#define ZEND_MM_BLOCK_SIZE(b)			((b)-&gt;info._size &amp; ~ZEND_MM_TYPE_MASK)</span>
<span style="color: #339933;">#define ZEND_MM_TYPE_MASK		ZEND_MM_LONG_CONST(0x3)</span></pre></td></tr></table></div>

<p>当前块的下一个元素，即为当前块的头位置加上整个块(去掉了类型的长度)的长度。<br />
当前块的上一个元素，即为当前块的头位置减去前一个块(去掉了类型的长度)的长度。<br />
关于前一个块的长度，在块的初始化时设置为当前块的大小与块类型的或操作的结果。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/11/php-source-code-31-memory-pool-heap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
