nginx+php-fpm模式php内存泄漏探究

2018-12-26 16:12:56

之前遇到过一次服务器内存告警,查看后发现有个php-fpm进程占用了2G的内存。但我明明在php.ini文件里面,有配置 memory_limit = 256M,那为什么会有占用2G内存的php-fpm进程呢?


这里先简单说一下nginx+php-fpm模式的工作原理。 



nginx服务器fork出n个子进程(worker),php-fpm管理器fork出n个子进程。

当有用户请求,nginx的一个worker接收请求,并将请求抛到socket中。

php-fpm空闲的子进程监听到socket中有请求,接收并处理请求。

这里要重点说一下第三步骤。第三步涉及到php-fpm进程生命周期的东西。一个php-fpm的生命周期大致是这样的:模块初始化(MINIT)-> 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN) -> 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)……. 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)-> 模块关闭(MSHUTDOWN)。在一个php-fpm进程的生命周期里,会有多次的模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)的过程。这个“请求处理”的大致过程是这样的:php读取相应的php文件,对其进行词法分析,生成opcode,zend虚拟机执行opcode。


回到一开始说的PHP配置文件里面的memory_limit 这个东西,其实,它限制的只是这个“请求处理”的内存。所以,这个参数跟php-fpm进程占用的内存并没有什么关系。那为什么会有占用2G大小的php-fpm进程呢?原因是这样的:php是用c写的,所以,难免又会一些内存泄露。也就是说,在“请求处理”这个过程结束后,有些变量没有被销毁,然后就导致一个php-fpm进程占用的内存越来越大。


那么,有什么办法能阻止这个问题呢?方法一:写不泄漏内存的php程序;方法二:在php-fpm配置文件中,将pm.max_requests这个参数设置小一点。这个参数的含义是:一个php-fpm子进程最多处理pm.max_requests个用户请求后,就会被销毁。当一个php-fpm进程被销毁后,它所占用的所有内存都会被回收。


以下是我自己做的一次测试:


首先:将php-fpm.conf里面pm.max_chindren参数设为1。这意味着,php-fpm管理器只会fork出一个php-fpm子进程。这样一来,用户的每次请求,都会让这个进程来处理。然后随便请求我自己的网站,会看到有如下结果: 

 

可以看到,那个php-fpm子进程占用的内存越来越大。


然后我将php-fpm重启一下: 

 

可以看到php-fpm子进程占用的内存立刻变小了。


最后我将pm.max_requests这个参数设为5,然后再随便请求几次我的网站,看一下结果: 

 

可以看到,php-fpm子进程的进程id会自动变化。这说明,php-fpm子进程确实会自动被销毁,然后再产生新的php-fpm子进程,而且php-fpm子进程占用的内存也没有越来越大。


  • 2020-06-02 08:57:12

    clipboard复制成功但是粘贴板是空的

    将文本复制到剪贴板应该不难。配置它不需要几十个步骤,也不需要加载数百KB的js文件 。但最重要的是,它不应该依赖Flash或任何臃肿的框架。这就是clipboard.js存在的原因。

  • 2020-06-04 13:54:21

    vue生成的__ob__: Observer无法解析jsonp

    computed 从vuex获得数据,watch监听数据 然而问题就出现在了监听上,监听不到,给个setTimeOut 1000 就能检测到数据了,不然打印时又数据,用的时候时空的,不知道时什么原因。

  • 2020-06-06 20:22:56

    laravel 接收json串

    在做项目的时候发现 用平时的$request->all() 无法获取到请求值

  • 2020-06-09 08:50:28

    LRU原理以及js实现

    LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

  • 2020-06-20 06:31:16

    mac下全局配置adb环境

    不提示“command not found”,而是出现一长串帮助说明,那就证明adb已经配置好了。

  • 2020-06-20 06:31:39

    Android 无线调试手机(WiFi 调试)

    手机需要开启 USB 调试 手机和电脑要在同一个局域网(连接同一个 WiFi) adb connect 连接成功后要拔出 USB 线,不然出现同时连接两个设备的问题 执行命令 ”adb tcpip 6666“ 后可能需要重新开启 USB 调试

  • 2020-08-16 16:09:30

    android WebView 注入js 几种方式

    有时我们开发中需要将js 注入到我们本地,有可能你会说,放在Web不就可以了吗,的确,但是需求就是这样的