PHP脚本执行后内存未释放问题求助(LAMP环境)
核心问题分析
你当前用fetchAll(PDO::FETCH_ASSOC)一次性将100万行数据加载到PHP内存中,这本身就会占用大量内存;加上Apache进程模式下,PHP内存是进程级的,即使调用unset或gc_collect_cycles,内存也会被PHP内存池保留(用于后续请求复用),不会立即归还给系统,多次请求后内存累积导致进程崩溃。
有效解决方案
1. 逐行读取数据,避免一次性加载全量结果
放弃fetchAll,改用循环逐行获取数据,每次仅在内存中保留一行数据,处理完成后自动释放,内存占用会大幅降低:
$db = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass); $req = $db->prepare('SELECT open, high, low, close, volume, timestamp FROM '.$f_dataset); // 只查需要的字段 $req->execute(); // 逐行处理数据 while ($row = $req->fetch(PDO::FETCH_ASSOC)) { // 在这里执行单条蜡烛数据的处理逻辑(比如计算、写入文件等) } $req->closeCursor(); unset($req, $db);
2. 优化查询语句,只获取必要字段
不要用SELECT *,明确指定策略测试需要的字段(比如时间、开高低收、成交量),减少单条数据的内存占用,进一步降低整体内存消耗。
3. 调整Apache进程配置,强制释放内存
针对Apache的prefork模式,修改配置文件(httpd.conf或apache2.conf),设置子进程的请求上限和内存限制,让进程在达到阈值后自动重启,释放内存:
<IfModule mpm_prefork_module> MaxRequestPerChild 100 # 每个子进程处理100个请求后自动重启 RLimitMEM 768M # 限制单个进程最大内存占用,超过则被系统终止 </IfModule>
4. 改用PHP CLI模式运行脚本
如果交易策略测试工具不需要通过Web访问,直接用CLI模式执行脚本。脚本执行完成后进程会直接退出,系统会完全回收所有内存,不会出现内存累积问题:
php your_strategy_test_script.php
5. 启用PDO非缓冲查询
通过设置MYSQL_ATTR_USE_BUFFERED_QUERY => false,让PDO不将全量结果加载到PHP内存,而是直接从MySQL服务器逐行读取。注意:非缓冲查询下必须处理完所有结果才能执行下一个数据库操作,且无法使用rowCount()获取总行数:
$db = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass, [ PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false ]); $req = $db->prepare('SELECT open, high, low, close, volume, timestamp FROM '.$f_dataset); $req->execute(); while ($row = $req->fetch(PDO::FETCH_ASSOC)) { // 处理数据 } $req->closeCursor(); unset($req, $db);
为什么unset和gc_collect_cycles无效?
在Apache的进程模式下,PHP的内存由进程管理,unset只是标记变量为垃圾,gc_collect_cycles会回收PHP内存池中的垃圾,但这些内存不会立即归还给操作系统,而是被PHP保留用于后续请求。只有当Apache子进程结束时,操作系统才会回收该进程占用的所有内存。
内容的提问来源于stack exchange,提问作者geopra

