本文目录导读:

在PHP长驻脚本中,主动释放内存主要依赖以下几个策略:
变量销毁与复用
<?php
// 正确方式:显式销毁大变量
$bigData = fetchLargeData();
processData($bigData);
unset($bigData); // 立即释放
// 复用变量而不是赋值新变量
$buffer = '';
for ($i = 0; $i < 1000; $i++) {
$buffer .= getData(); // 累积使用
if (strlen($buffer) > 1_000_000) {
processBuffer($buffer);
$buffer = ''; // 清空被回收
}
}
循环引用处理
<?php
// 手动解除循环引用
function createCircularReference() {
$a = new stdClass();
$b = new stdClass();
$a->ref = $b;
$b->ref = $a;
// 处理完数据后
$a->ref = null; // 解除引用
$b->ref = null;
// 或者 unset($a, $b);
}
// 使用 weak references (PHP 7.4+)
$object = new stdClass();
$weakRef = WeakReference::create($object);
unset($object); // 对象可被回收
内存缓存管理
<?php
class MemoryManager {
private array $cache = [];
private int $maxSize;
private int $currentSize = 0;
public function __construct(int $maxSizeMB = 50) {
$this->maxSize = $maxSizeMB * 1024 * 1024;
}
public function set(string $key, string $value): void {
$size = strlen($value);
// 检查内存阈值
if ($this->currentSize + $size > $this->maxSize) {
$this->evict(0.2); // 清除20%缓存
}
$this->cache[$key] = $value;
$this->currentSize += $size;
}
public function evict(float $percentage): void {
$removeCount = (int)(count($this->cache) * $percentage);
$keys = array_keys($this->cache);
for ($i = 0; $i < $removeCount && $i < count($keys); $i++) {
$key = $keys[$i];
$this->currentSize -= strlen($this->cache[$key]);
unset($this->cache[$key]);
}
}
public function clear(): void {
$this->cache = [];
$this->currentSize = 0;
}
}
使用生成器处理大数据
<?php
// 不使用生成器(会占用大量内存)
function getLargeData(): array {
$data = [];
for ($i = 0; $i < 1000000; $i++) {
$data[] = processItem($i);
}
return $data;
}
// 使用生成器(逐项处理,内存友好)
function getLargeDataGenerator(): Generator {
for ($i = 0; $i < 1000000; $i++) {
yield processItem($i);
}
}
// 使用方式
foreach (getLargeDataGenerator() as $item) {
processItem($item);
}
垃圾回收主动控制
<?php
class GarbageCollector {
private int $processedCount = 0;
private int $collectInterval = 100;
public function afterProcess(): void {
$this->processedCount++;
if ($this->processedCount % $this->collectInterval === 0) {
// 主动触发垃圾回收
if (gc_enabled()) {
$collected = gc_collect_cycles();
$this->log("Collected $collected cycles");
}
// 重置计数器
$this->processedCount = 0;
}
}
// 内存阈值触发回收
public function checkThreshold(int $thresholdMB = 100): void {
$memoryUsage = memory_get_usage(true);
if ($memoryUsage > $thresholdMB * 1024 * 1024) {
$collected = gc_collect_cycles();
$this->log("Memory above threshold, collected $collected cycles");
}
}
private function log(string $message): void {
echo date('Y-m-d H:i:s') . " - $message\n";
}
}
完整的长驻脚本示例
<?php
class LongRunningProcess {
private PDO $db;
private GarbageCollector $gc;
private MemoryManager $cache;
private int $maxMemoryMB = 100;
public function __construct() {
$this->db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->gc = new GarbageCollector();
$this->cache = new MemoryManager(30);
// 关闭默认的垃圾回收阈值检查
gc_disable();
}
public function run(): void {
while (true) {
try {
$this->processBatch();
$this->gc->afterProcess();
$this->checkAndReleaseMemory();
$this->reconnectDatabase();
} catch (Exception $e) {
$this->handleError($e);
}
// 短暂休眠避免CPU 100%
usleep(100000); // 100ms
}
}
private function processBatch(): void {
// 使用语句预编译和逐行fetch
$stmt = $this->db->prepare("SELECT * FROM large_table WHERE processed = 0 LIMIT 100");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// 处理单行数据
$result = $this->processRow($row);
// 缓存必要结果时限制大小
if (strlen($result) < 1024 * 100) { // <100KB
$this->cache->set($row['id'], $result);
}
// 立即释放行数据
unset($row);
unset($result);
}
// 释放语句资源
$stmt->closeCursor();
unset($stmt);
}
private function checkAndReleaseMemory(): void {
$currentMemory = memory_get_usage(true);
if ($currentMemory > $this->maxMemoryMB * 1024 * 1024) {
// 清理缓存
$this->cache->evict(0.5);
// 强制垃圾回收
gc_collect_cycles();
// 如果有APCu等缓存,可以清理部分
// apcu_clear_cache();
echo sprintf(
"Memory reclaimed: %s -> %s\n",
$this->formatBytes($currentMemory),
$this->formatBytes(memory_get_usage(true))
);
}
}
private function reconnectDatabase(): void {
// 定期重连避免连接超时
static $lastReconnect = 0;
if (time() - $lastReconnect > 300) { // 每5分钟重连
$this->db = null;
$this->db = new PDO(/* 连接参数 */);
$lastReconnect = time();
}
}
private function handleError(Exception $e): void {
echo "Error: " . $e->getMessage() . "\n";
// 确保资源释放
gc_collect_cycles();
}
private function formatBytes(int $bytes): string {
return round($bytes / 1024 / 1024, 2) . 'MB';
}
}
// 启动脚本
$process = new LongRunningProcess();
$process->run();
监控和调试工具
<?php
// 内存监控封装
class MemoryMonitor {
private array $checkpoints = [];
public function checkpoint(string $label): void {
$this->checkpoints[] = [
'label' => $label,
'time' => microtime(true),
'memory' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
];
}
public function report(): void {
foreach ($this->checkpoints as $cp) {
echo sprintf(
"[%s] Memory: %s (Peak: %s)\n",
$cp['label'],
$this->formatBytes($cp['memory']),
$this->formatBytes($cp['peak'])
);
}
}
private function formatBytes(int $bytes): string {
return round($bytes / 1024 / 1024, 2) . 'MB';
}
}
// 使用方式
$monitor = new MemoryMonitor();
$monitor->checkpoint('start');
// ... 处理逻辑 ...
$monitor->checkpoint('after processing');
$monitor->report();
关键原则
- 及时释放引用:使用
unset()释放大对象 - 避免创建长期引用:特别是对象间的循环引用
- 使用生成器:处理大数据集时逐项处理
- 分批次处理:避免一次性加载过多数据
- 手动控制GC:在适当时候调用
gc_collect_cycles() - 设置内存限制:使用
memory_limit和内存检查 - 监控内存变化:定期记录和分析内存使用情况
通过这些策略结合使用,可以有效管理PHP长驻脚本的内存使用,避免内存泄漏和性能下降。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播