这篇文章,我们来实战操作一下扩展的内存管理,感受一下内存泄漏,加深对PHP
内存管理的理解。
首先,创建扩展目录:
1 | ~/codeDir/cCode/php-7.1.0/ext # ./ext_skel --extname=memory |
然后进入目录:
1 | ~/codeDir/cCode/php-7.1.0/ext # cd memory/ |
替换文件config.m4
为如下内容:
1 | PHP_ARG_ENABLE(memory, whether to enable memory support, |
然后编辑文件memory.c
里面的PHP_FUNCTION(confirm_memory_compiled)
方法:
1 | PHP_FUNCTION(confirm_memory_compiled) |
这里,我们定义了一个PHP
函数confirm_memory_compiled
。它做的事情很简单,就是从堆中申请一块2M
的内存,并且没有主动释放。
接着,编译、安装扩展:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # phpize ; ./configure |
然后把扩展加入配置文件里面:
1 | extension=memory.so |
然后确认是否安装扩展成功:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # php --ri memory |
然后编写测试脚本:
1 |
|
执行脚本:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # php memory.php |
没有报错,说明我们的脚本正常执行了。
接着,我们启动一个PHP
自带的服务器:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # php -S 127.0.0.1:80 -t ./ |
然后,另起一个终端,执行top
命令,用来观察PHP
进程的内存使用情况:
1 | 35247 root 20 0 27.8m 14.7m 0.0 0.7 0:00.02 S `- php -S 127.0.0.1:80 -t ./ |
可以看到,在启动服务器的时候,PHP
占用了27.8M
的内存。
我们请求一次我们的服务器:
1 | ~/codeDir/cppCode/study # curl 127.0.0.1/memory.php |
然后查看PHP
占的内存:
1 | 13713 root 20 0 29.8m 14.9m 0.0 0.7 0:00.01 S `- php -S 127.0.0.1:80 -t ./ |
我们发现PHP
多占了2M
的内存。
我们再请求一次:
1 | ~/codeDir/cppCode/study # curl 127.0.0.1/memory.php |
然后再次查看PHP
占的内存:
1 | 13713 root 20 0 31.8m 15.0m 0.0 0.8 0:00.03 S `- php -S 127.0.0.1:80 -t ./ |
我们发现PHP
又多占了2M
的内存。
所以说,如果我们在一次请求的生命周期通过malloc
分配了内存,但是没有释放,那么就会造成PHP
整个生命周期的内存泄漏。
我们修改扩展函数:
1 | PHP_FUNCTION(confirm_memory_compiled) |
然后,重新编译、安装扩展:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # make clean ; make ; make install |
重新启动服务器:
1 | ~/codeDir/cCode/php-7.1.0/ext/memory # php -S 127.0.0.1:80 -t ./ |
此时,PHP
进程占用的内存:
1 | 14470 root 20 0 27.8m 14.6m 0.0 0.7 0:00.01 S `- php -S 127.0.0.1:80 -t ./ |
然后,我们请求一次服务器:
1 | ~/codeDir/cppCode/study # curl 127.0.0.1/memory.php |
查看PHP
内存占用情况:
1 | 14470 root 20 0 27.8m 15.2m 0.0 0.8 0:00.02 S `- php -S 127.0.0.1:80 -t ./ |
发现,没有增长。
再次请求服务器:
1 | ~/codeDir/cppCode/study # curl 127.0.0.1/memory.php |
再次查看PHP
内存占用情况:
1 | 14470 root 20 0 27.8m 15.2m 0.7 0.8 0:00.03 S `- php -S 127.0.0.1:80 -t ./ |
发现还是没有增长。
所以说,如果我们在一次请求的生命周期中通过emalloc
分配了内存,但是没有释放,那么在PHP
整个生命周期是不会造成内存泄漏的。因为在请求结束的时候,PHP
会自动帮我们释放掉这些内存。但是,在一次请求中,如果一直不自己释放内存,那么这次请求很可能会内存不够,导致PHP
进程挂掉。
以上对malloc
和emalloc
的分析适用于FPM
模式。但是对于Swoole
这类扩展,接管了PHP
的请求生命周期,所有对Swoole
的请求都是在同一个请求生命周期里面,并且,这个请求生命周期一直不会结束。所以,就算我们使用了emalloc
这类内存管理器,如果没有主动释放,也是会造成内存泄漏的,因为此时PHP
的请求生命周期不会结束,因此PHP
不会自己帮我们去释放这些内存。