PHPアプリケーションのメモリ使用量を監視し、パフォーマンスを最適化するために重要なmemory_get_peak_usage
関数について、基本的な使い方から実際の活用例まで詳しく解説します。メモリ効率の良いプログラムを作成して、安定したアプリケーション運用を実現しましょう。
memory_get_peak_usage関数とは?
memory_get_peak_usage
は、PHPスクリプトの実行開始からその時点までの間に使用されたメモリの最大値(ピークメモリ使用量)を取得する関数です。メモリリークの検出やパフォーマンス分析において、memory_get_usage
関数と並んで重要な役割を果たします。
基本構文
memory_get_peak_usage(bool $real_usage = false): int
パラメータ:
$real_usage
: メモリ使用量の種類を指定false
(デフォルト): PHPが使用しているメモリ量true
: システムから実際に割り当てられたメモリ量
戻り値:
- メモリ使用量(バイト単位の整数値)
memory_get_usage関数との違い
// 現在のメモリ使用量
echo "現在のメモリ使用量: " . memory_get_usage() . " bytes\n";
// ピークメモリ使用量(これまでの最大値)
echo "ピークメモリ使用量: " . memory_get_peak_usage() . " bytes\n";
// 大量のメモリを使用する処理
$largeArray = range(1, 100000);
echo "処理後の現在メモリ使用量: " . memory_get_usage() . " bytes\n";
echo "処理後のピークメモリ使用量: " . memory_get_peak_usage() . " bytes\n";
// 配列を削除
unset($largeArray);
echo "削除後の現在メモリ使用量: " . memory_get_usage() . " bytes\n";
echo "削除後のピークメモリ使用量: " . memory_get_peak_usage() . " bytes\n";
// ピーク値は変わらない(これまでの最大値を記録)
基本的な使用例
1. メモリ使用量の基本的な確認
// スクリプト開始時のメモリ状況
echo "=== 初期メモリ状況 ===\n";
echo "現在のメモリ使用量: " . formatBytes(memory_get_usage()) . "\n";
echo "ピークメモリ使用量: " . formatBytes(memory_get_peak_usage()) . "\n";
echo "システム割り当てメモリ: " . formatBytes(memory_get_usage(true)) . "\n";
echo "システム割り当てピーク: " . formatBytes(memory_get_peak_usage(true)) . "\n\n";
function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
// 処理を実行
$data = [];
for ($i = 0; $i < 50000; $i++) {
$data[] = "データ項目 " . $i;
}
echo "=== 処理後のメモリ状況 ===\n";
echo "現在のメモリ使用量: " . formatBytes(memory_get_usage()) . "\n";
echo "ピークメモリ使用量: " . formatBytes(memory_get_peak_usage()) . "\n";
2. real_usageパラメータの比較
echo "=== PHPメモリ vs システムメモリ ===\n";
echo "PHP認識メモリ使用量: " . formatBytes(memory_get_peak_usage(false)) . "\n";
echo "システム実際メモリ: " . formatBytes(memory_get_peak_usage(true)) . "\n";
$difference = memory_get_peak_usage(true) - memory_get_peak_usage(false);
echo "差分: " . formatBytes($difference) . "\n";
echo "システムオーバーヘッド: " .
number_format(($difference / memory_get_peak_usage(false)) * 100, 2) . "%\n";
実践的な活用例
1. メモリ使用量監視システム
class MemoryMonitor
{
private $checkpoints = [];
private $alerts = [];
private $memoryLimit;
public function __construct($alertThreshold = 0.8)
{
$this->memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
$this->alertThreshold = $alertThreshold;
// 初期チェックポイントを記録
$this->checkpoint('initialization');
}
/**
* メモリ使用状況のチェックポイントを作成
*/
public function checkpoint($label)
{
$current = memory_get_usage();
$peak = memory_get_peak_usage();
$currentReal = memory_get_usage(true);
$peakReal = memory_get_peak_usage(true);
$checkpoint = [
'label' => $label,
'timestamp' => microtime(true),
'current_memory' => $current,
'peak_memory' => $peak,
'current_memory_real' => $currentReal,
'peak_memory_real' => $peakReal,
'memory_limit' => $this->memoryLimit,
'usage_percentage' => ($peak / $this->memoryLimit) * 100
];
$this->checkpoints[] = $checkpoint;
// アラートチェック
$this->checkMemoryAlert($checkpoint);
return $checkpoint;
}
/**
* メモリアラートのチェック
*/
private function checkMemoryAlert($checkpoint)
{
if ($checkpoint['usage_percentage'] > ($this->alertThreshold * 100)) {
$alert = [
'timestamp' => $checkpoint['timestamp'],
'label' => $checkpoint['label'],
'usage_percentage' => $checkpoint['usage_percentage'],
'peak_memory' => $checkpoint['peak_memory'],
'message' => sprintf(
'メモリ使用量が警告レベルに達しました: %.2f%% (%s)',
$checkpoint['usage_percentage'],
$this->formatBytes($checkpoint['peak_memory'])
)
];
$this->alerts[] = $alert;
// ログ出力
error_log($alert['message']);
}
}
/**
* メモリ使用量の詳細レポート生成
*/
public function generateReport()
{
if (empty($this->checkpoints)) {
return "チェックポイントがありません。";
}
$latest = end($this->checkpoints);
$first = reset($this->checkpoints);
$report = "=== メモリ使用量監視レポート ===\n\n";
$report .= "監視期間: " . date('Y-m-d H:i:s', $first['timestamp']) .
" ~ " . date('Y-m-d H:i:s', $latest['timestamp']) . "\n";
$report .= "チェックポイント数: " . count($this->checkpoints) . "\n";
$report .= "アラート数: " . count($this->alerts) . "\n\n";
$report .= "=== 現在のメモリ状況 ===\n";
$report .= "現在のメモリ使用量: " . $this->formatBytes($latest['current_memory']) . "\n";
$report .= "ピークメモリ使用量: " . $this->formatBytes($latest['peak_memory']) . "\n";
$report .= "メモリ制限: " . $this->formatBytes($this->memoryLimit) . "\n";
$report .= "使用率: " . number_format($latest['usage_percentage'], 2) . "%\n\n";
// メモリ増加の傾向分析
$memoryGrowth = $latest['peak_memory'] - $first['peak_memory'];
$report .= "=== メモリ使用量の変化 ===\n";
$report .= "メモリ増加量: " . $this->formatBytes($memoryGrowth) . "\n";
if ($memoryGrowth > 0) {
$timeSpan = $latest['timestamp'] - $first['timestamp'];
$growthRate = $memoryGrowth / max($timeSpan, 1);
$report .= "増加率: " . $this->formatBytes($growthRate) . "/秒\n";
}
// チェックポイント詳細
$report .= "\n=== チェックポイント詳細 ===\n";
foreach ($this->checkpoints as $cp) {
$report .= sprintf(
"%-20s: %s (使用率: %.2f%%)\n",
$cp['label'],
$this->formatBytes($cp['peak_memory']),
$cp['usage_percentage']
);
}
// アラート詳細
if (!empty($this->alerts)) {
$report .= "\n=== アラート詳細 ===\n";
foreach ($this->alerts as $alert) {
$report .= date('Y-m-d H:i:s', $alert['timestamp']) .
" - " . $alert['message'] . "\n";
}
}
return $report;
}
/**
* メモリリークの検出
*/
public function detectMemoryLeaks($threshold = 1048576) // 1MB
{
if (count($this->checkpoints) < 2) {
return [];
}
$leaks = [];
for ($i = 1; $i < count($this->checkpoints); $i++) {
$current = $this->checkpoints[$i];
$previous = $this->checkpoints[$i - 1];
$growth = $current['peak_memory'] - $previous['peak_memory'];
if ($growth > $threshold) {
$leaks[] = [
'from' => $previous['label'],
'to' => $current['label'],
'growth' => $growth,
'growth_formatted' => $this->formatBytes($growth)
];
}
}
return $leaks;
}
private function parseMemoryLimit($limit)
{
if ($limit === '-1') {
return PHP_INT_MAX;
}
$unit = strtolower(substr($limit, -1));
$value = (int)$limit;
switch ($unit) {
case 'g': return $value * 1024 * 1024 * 1024;
case 'm': return $value * 1024 * 1024;
case 'k': return $value * 1024;
default: return $value;
}
}
private function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
}
// 使用例
$monitor = new MemoryMonitor(0.7); // 70%でアラート
// 各処理でチェックポイントを作成
$monitor->checkpoint('データ読み込み開始');
// 大量データ処理のシミュレート
$largeData = [];
for ($i = 0; $i < 100000; $i++) {
$largeData[] = [
'id' => $i,
'name' => 'User ' . $i,
'email' => 'user' . $i . '@example.com',
'data' => str_repeat('x', 100)
];
}
$monitor->checkpoint('データ生成完了');
// データ処理
$processedData = array_map(function($item) {
return $item['name'] . ' - ' . $item['email'];
}, $largeData);
$monitor->checkpoint('データ処理完了');
// メモリクリーンアップ
unset($largeData);
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
$monitor->checkpoint('クリーンアップ完了');
// レポート出力
echo $monitor->generateReport();
// メモリリーク検出
$leaks = $monitor->detectMemoryLeaks();
if (!empty($leaks)) {
echo "\n=== 検出されたメモリリーク ===\n";
foreach ($leaks as $leak) {
echo "{$leak['from']} → {$leak['to']}: {$leak['growth_formatted']}\n";
}
}
2. 大量データ処理の最適化
class OptimizedDataProcessor
{
private $batchSize;
private $memoryLimit;
public function __construct($batchSize = 1000, $memoryLimitMB = 100)
{
$this->batchSize = $batchSize;
$this->memoryLimit = $memoryLimitMB * 1024 * 1024;
}
/**
* メモリ効率を考慮した大量データ処理
*/
public function processLargeDataset($dataSource, $processor)
{
$results = [];
$processedCount = 0;
$batch = [];
$initialMemory = memory_get_peak_usage();
echo "=== 大量データ処理開始 ===\n";
echo "初期メモリ使用量: " . $this->formatBytes($initialMemory) . "\n";
echo "バッチサイズ: {$this->batchSize}\n";
echo "メモリ制限: " . $this->formatBytes($this->memoryLimit) . "\n\n";
foreach ($dataSource as $item) {
$batch[] = $item;
// バッチサイズに達した場合、またはメモリ使用量が制限に近づいた場合
if (count($batch) >= $this->batchSize ||
$this->isMemoryLimitApproaching()) {
// バッチ処理実行
$batchResults = $this->processBatch($batch, $processor);
$results = array_merge($results, $batchResults);
$processedCount += count($batch);
// メモリ状況レポート
$this->reportMemoryStatus($processedCount);
// バッチをクリア
$batch = [];
// ガベージコレクション実行
$this->performGarbageCollection();
}
}
// 残りのデータを処理
if (!empty($batch)) {
$batchResults = $this->processBatch($batch, $processor);
$results = array_merge($results, $batchResults);
$processedCount += count($batch);
}
$finalMemory = memory_get_peak_usage();
echo "\n=== 処理完了 ===\n";
echo "処理件数: {$processedCount}\n";
echo "最終メモリ使用量: " . $this->formatBytes($finalMemory) . "\n";
echo "メモリ増加量: " . $this->formatBytes($finalMemory - $initialMemory) . "\n";
return $results;
}
private function processBatch($batch, $processor)
{
$batchStart = memory_get_peak_usage();
$results = [];
foreach ($batch as $item) {
$results[] = $processor($item);
}
$batchEnd = memory_get_peak_usage();
$batchMemoryUsage = $batchEnd - $batchStart;
if ($batchMemoryUsage > 0) {
echo " バッチ処理メモリ増加: " .
$this->formatBytes($batchMemoryUsage) . "\n";
}
return $results;
}
private function isMemoryLimitApproaching($threshold = 0.8)
{
$currentUsage = memory_get_peak_usage();
return $currentUsage > ($this->memoryLimit * $threshold);
}
private function reportMemoryStatus($processedCount)
{
$currentMemory = memory_get_usage();
$peakMemory = memory_get_peak_usage();
echo "処理済み: {$processedCount}, " .
"現在メモリ: " . $this->formatBytes($currentMemory) . ", " .
"ピークメモリ: " . $this->formatBytes($peakMemory) . "\n";
}
private function performGarbageCollection()
{
$beforeGC = memory_get_usage();
if (function_exists('gc_collect_cycles')) {
$cycles = gc_collect_cycles();
$afterGC = memory_get_usage();
$freed = $beforeGC - $afterGC;
if ($freed > 0) {
echo " GC実行: {$cycles}サイクル, " .
$this->formatBytes($freed) . " 解放\n";
}
}
}
private function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
}
// 使用例
$detector = new MemoryLeakDetector();
$detector->sample('初期状態');
// 処理1: 通常の処理
$data1 = range(1, 10000);
$detector->sample('処理1完了');
// 処理2: メモリを大量消費する処理
$data2 = [];
for ($i = 0; $i < 50000; $i++) {
$data2[] = str_repeat('x', 100);
}
$detector->sample('処理2完了');
// 処理3: データクリーンアップ
unset($data1, $data2);
$detector->sample('クリーンアップ後');
// ガベージコレクション
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
$detector->sample('GC後');
echo $detector->getReport();
2. メモリプロファイラクラス
class AdvancedMemoryProfiler
{
private $profiles = [];
private $activeProfiles = [];
/**
* プロファイリング開始
*/
public function startProfiling($name)
{
$this->activeProfiles[$name] = [
'start_time' => microtime(true),
'start_memory' => memory_get_usage(),
'start_peak' => memory_get_peak_usage(),
'start_memory_real' => memory_get_usage(true),
'start_peak_real' => memory_get_peak_usage(true)
];
return $this;
}
/**
* プロファイリング終了
*/
public function endProfiling($name)
{
if (!isset($this->activeProfiles[$name])) {
throw new InvalidArgumentException("プロファイル '{$name}' は開始されていません");
}
$start = $this->activeProfiles[$name];
$endTime = microtime(true);
$profile = [
'name' => $name,
'execution_time' => $endTime - $start['start_time'],
'memory_usage' => [
'start' => $start['start_memory'],
'end' => memory_get_usage(),
'peak' => memory_get_peak_usage(),
'increase' => memory_get_usage() - $start['start_memory'],
'peak_increase' => memory_get_peak_usage() - $start['start_peak']
],
'memory_usage_real' => [
'start' => $start['start_memory_real'],
'end' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
'increase' => memory_get_usage(true) - $start['start_memory_real'],
'peak_increase' => memory_get_peak_usage(true) - $start['start_peak_real']
]
];
$this->profiles[] = $profile;
unset($this->activeProfiles[$name]);
return $profile;
}
/**
* 関数の実行をプロファイル
*/
public function profile($name, callable $callback)
{
$this->startProfiling($name);
try {
$result = $callback();
$profile = $this->endProfiling($name);
return [
'result' => $result,
'profile' => $profile
];
} catch (Exception $e) {
$this->endProfiling($name);
throw $e;
}
}
/**
* 詳細レポート生成
*/
public function generateDetailedReport()
{
if (empty($this->profiles)) {
return "プロファイルデータがありません。";
}
$report = "=== 詳細メモリプロファイルレポート ===\n\n";
// 総合統計
$totalTime = array_sum(array_column($this->profiles, 'execution_time'));
$totalMemoryIncrease = array_sum(array_column(
array_column($this->profiles, 'memory_usage'),
'increase'
));
$report .= "総実行時間: " . number_format($totalTime * 1000, 2) . " ms\n";
$report .= "総メモリ増加: " . $this->formatBytes($totalMemoryIncrease) . "\n";
$report .= "プロファイル数: " . count($this->profiles) . "\n\n";
// 個別プロファイル詳細
$report .= "=== 個別プロファイル詳細 ===\n";
foreach ($this->profiles as $profile) {
$report .= $this->formatProfileReport($profile) . "\n";
}
// ランキング
$report .= $this->generateRankings();
return $report;
}
private function formatProfileReport($profile)
{
$memory = $profile['memory_usage'];
$memoryReal = $profile['memory_usage_real'];
$report = "--- {$profile['name']} ---\n";
$report .= "実行時間: " . number_format($profile['execution_time'] * 1000, 2) . " ms\n";
$report .= "メモリ使用量:\n";
$report .= " 開始: " . $this->formatBytes($memory['start']) . "\n";
$report .= " 終了: " . $this->formatBytes($memory['end']) . "\n";
$report .= " ピーク: " . $this->formatBytes($memory['peak']) . "\n";
$report .= " 増加: " . $this->formatBytes($memory['increase']) . "\n";
$report .= " ピーク増加: " . $this->formatBytes($memory['peak_increase']) . "\n";
$report .= "システムメモリ:\n";
$report .= " 増加: " . $this->formatBytes($memoryReal['increase']) . "\n";
$report .= " ピーク増加: " . $this->formatBytes($memoryReal['peak_increase']) . "\n";
return $report;
}
private function generateRankings()
{
$report = "=== ランキング ===\n";
// 実行時間ランキング
$timeRanking = $this->profiles;
usort($timeRanking, function($a, $b) {
return $b['execution_time'] <=> $a['execution_time'];
});
$report .= "\n実行時間TOP 5:\n";
for ($i = 0; $i < min(5, count($timeRanking)); $i++) {
$profile = $timeRanking[$i];
$report .= sprintf(
"%d. %s: %s ms\n",
$i + 1,
$profile['name'],
number_format($profile['execution_time'] * 1000, 2)
);
}
// メモリ使用量ランキング
$memoryRanking = $this->profiles;
usort($memoryRanking, function($a, $b) {
return $b['memory_usage']['peak_increase'] <=> $a['memory_usage']['peak_increase'];
});
$report .= "\nメモリ使用量TOP 5:\n";
for ($i = 0; $i < min(5, count($memoryRanking)); $i++) {
$profile = $memoryRanking[$i];
$report .= sprintf(
"%d. %s: %s\n",
$i + 1,
$profile['name'],
$this->formatBytes($profile['memory_usage']['peak_increase'])
);
}
return $report;
}
/**
* メモリ効率の分析
*/
public function analyzeMemoryEfficiency()
{
$analysis = [
'total_profiles' => count($this->profiles),
'memory_efficient' => 0,
'memory_heavy' => 0,
'time_efficient' => 0,
'time_heavy' => 0,
'recommendations' => []
];
if (empty($this->profiles)) {
return $analysis;
}
// 基準値計算
$avgMemoryIncrease = array_sum(array_column(
array_column($this->profiles, 'memory_usage'),
'peak_increase'
)) / count($this->profiles);
$avgExecutionTime = array_sum(array_column($this->profiles, 'execution_time')) / count($this->profiles);
foreach ($this->profiles as $profile) {
$memoryIncrease = $profile['memory_usage']['peak_increase'];
$executionTime = $profile['execution_time'];
// メモリ効率の判定
if ($memoryIncrease < $avgMemoryIncrease * 0.5) {
$analysis['memory_efficient']++;
} elseif ($memoryIncrease > $avgMemoryIncrease * 2) {
$analysis['memory_heavy']++;
$analysis['recommendations'][] = "'{$profile['name']}'のメモリ使用量を最適化してください";
}
// 時間効率の判定
if ($executionTime < $avgExecutionTime * 0.5) {
$analysis['time_efficient']++;
} elseif ($executionTime > $avgExecutionTime * 2) {
$analysis['time_heavy']++;
$analysis['recommendations'][] = "'{$profile['name']}'の実行時間を短縮してください";
}
}
return $analysis;
}
private function formatBytes($size)
{
if ($size < 0) $size = 0;
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
}
// 使用例
$profiler = new AdvancedMemoryProfiler();
// 複数の処理をプロファイル
$result1 = $profiler->profile('配列処理', function() {
$arr = range(1, 100000);
return array_sum($arr);
});
$result2 = $profiler->profile('文字列処理', function() {
$str = '';
for ($i = 0; $i < 10000; $i++) {
$str .= 'Hello World ' . $i . "\n";
}
return strlen($str);
});
$result3 = $profiler->profile('オブジェクト処理', function() {
$objects = [];
for ($i = 0; $i < 50000; $i++) {
$objects[] = new stdClass();
$objects[$i]->id = $i;
$objects[$i]->data = str_repeat('x', 50);
}
return count($objects);
});
// レポート生成
echo $profiler->generateDetailedReport();
// 効率分析
$analysis = $profiler->analyzeMemoryEfficiency();
echo "\n=== 効率分析 ===\n";
echo "メモリ効率的: {$analysis['memory_efficient']}/{$analysis['total_profiles']}\n";
echo "メモリ重い: {$analysis['memory_heavy']}/{$analysis['total_profiles']}\n";
echo "時間効率的: {$analysis['time_efficient']}/{$analysis['total_profiles']}\n";
echo "時間重い: {$analysis['time_heavy']}/{$analysis['total_profiles']}\n";
if (!empty($analysis['recommendations'])) {
echo "\n推奨事項:\n";
foreach ($analysis['recommendations'] as $recommendation) {
echo "- {$recommendation}\n";
}
}
注意点とベストプラクティス
1. memory_limit設定の確認と調整
function checkMemoryConfiguration()
{
echo "=== PHPメモリ設定確認 ===\n";
$memoryLimit = ini_get('memory_limit');
echo "memory_limit: {$memoryLimit}\n";
if ($memoryLimit === '-1') {
echo "メモリ制限なし(注意が必要)\n";
} else {
$limitBytes = parseMemoryLimit($memoryLimit);
$currentUsage = memory_get_peak_usage(true);
$usagePercentage = ($currentUsage / $limitBytes) * 100;
echo "現在の使用量: " . formatBytes($currentUsage) .
" ({$usagePercentage:.2f}%)\n";
if ($usagePercentage > 80) {
echo "警告: メモリ使用量が80%を超えています\n";
}
}
echo "max_execution_time: " . ini_get('max_execution_time') . "秒\n";
echo "post_max_size: " . ini_get('post_max_size') . "\n";
echo "upload_max_filesize: " . ini_get('upload_max_filesize') . "\n";
}
function parseMemoryLimit($limit)
{
if ($limit === '-1') return PHP_INT_MAX;
$unit = strtolower(substr($limit, -1));
$value = (int)$limit;
switch ($unit) {
case 'g': return $value * 1024 * 1024 * 1024;
case 'm': return $value * 1024 * 1024;
case 'k': return $value * 1024;
default: return $value;
}
}
checkMemoryConfiguration();
2. メモリ使用量の最適化テクニック
class MemoryOptimizationTips
{
/**
* 配列処理の最適化例
*/
public static function demonstrateArrayOptimization()
{
echo "=== 配列処理の最適化 ===\n";
// 非効率な方法
$start = memory_get_peak_usage();
$inefficient = [];
for ($i = 0; $i < 100000; $i++) {
$inefficient[] = "Item " . $i;
}
$inefficientMemory = memory_get_peak_usage() - $start;
unset($inefficient);
// 効率的な方法(ジェネレータ使用)
$start = memory_get_peak_usage();
$count = 0;
foreach (self::generateItems(100000) as $item) {
$count++;
// 必要に応じて処理
}
$efficientMemory = memory_get_peak_usage() - $start;
echo "非効率な配列: " . formatBytes($inefficientMemory) . "\n";
echo "ジェネレータ: " . formatBytes($efficientMemory) . "\n";
echo "節約量: " . formatBytes($inefficientMemory - $efficientMemory) . "\n";
}
private static function generateItems($count)
{
for ($i = 0; $i < $count; $i++) {
yield "Item " . $i;
}
}
/**
* 文字列結合の最適化例
*/
public static function demonstrateStringOptimization()
{
echo "\n=== 文字列結合の最適化 ===\n";
// 非効率な方法
$start = memory_get_peak_usage();
$inefficient = '';
for ($i = 0; $i < 10000; $i++) {
$inefficient .= "Line " . $i . "\n";
}
$inefficientMemory = memory_get_peak_usage() - $start;
unset($inefficient);
gc_collect_cycles();
// 効率的な方法
$start = memory_get_peak_usage();
$efficient = [];
for ($i = 0; $i < 10000; $i++) {
$efficient[] = "Line " . $i;
}
$result = implode("\n", $efficient);
$efficientMemory = memory_get_peak_usage() - $start;
echo "非効率な結合: " . formatBytes($inefficientMemory) . "\n";
echo "効率的な結合: " . formatBytes($efficientMemory) . "\n";
echo "節約量: " . formatBytes($inefficientMemory - $efficientMemory) . "\n";
unset($efficient, $result);
}
}
// 最適化例の実行
MemoryOptimizationTips::demonstrateArrayOptimization();
MemoryOptimizationTips::demonstrateStringOptimization();
function formatBytes($size) {
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
まとめ
memory_get_peak_usage
関数は、PHPアプリケーションのメモリ使用量を監視し、パフォーマンスを最適化するために不可欠な関数です。ピークメモリ使用量を追跡することで、メモリリークの検出や処理の最適化を効率的に行うことができます。
重要なポイント:
memory_get_usage()
との組み合わせで包括的なメモリ監視が可能real_usage
パラメータでシステムレベルのメモリ使用量も確認- 大量データ処理時のバッチ処理とメモリ制限の考慮
- 定期的なガベージコレクションによるメモリ最適化
- プロファイリングツールの活用で効率的な分析が可能
- ジェネレータや適切なデータ構造の選択でメモリ使用量を削減
これらの知識と実践例を活用することで、メモリ効率が良く、スケーラブルなPHPアプリケーションの開発が可能になるでしょう。) / 3); return sprintf(“%.2f %s”, $size / pow(1024, $factor), $units[$factor]); } }
// 使用例 $processor = new OptimizedDataProcessor(500, 50); // バッチサイズ500, メモリ制限50MB
// 大量データのシミュレート(ジェネレータを使用してメモリ効率化) function generateLargeDataset($count) { for ($i = 0; $i < $count; $i++) { yield [ ‘id’ => $i, ‘data’ => str_repeat(‘x’, 1000), // 1KB per record ‘timestamp’ => time() + $i ]; } }
// データ処理関数 $dataProcessor = function($item) { return [ ‘id’ => $item[‘id’], ‘processed_data’ => strtoupper($item[‘data’]), ‘processed_at’ => date(‘Y-m-d H:i:s’, $item[‘timestamp’]) ]; };
// 大量データ処理実行 $results = $processor->processLargeDataset( generateLargeDataset(10000), $dataProcessor );
echo “結果配列のサイズ: ” . count($results) . “\n”;
### 3. ファイル処理時のメモリ監視
```php
class FileProcessorWithMemoryMonitoring
{
private $memoryThreshold;
public function __construct($memoryThresholdMB = 32)
{
$this->memoryThreshold = $memoryThresholdMB * 1024 * 1024;
}
/**
* 大きなファイルをメモリ効率良く処理
*/
public function processLargeFile($filePath, $lineProcessor)
{
if (!file_exists($filePath)) {
throw new InvalidArgumentException("ファイルが存在しません: {$filePath}");
}
$fileSize = filesize($filePath);
$initialMemory = memory_get_peak_usage();
echo "=== ファイル処理開始 ===\n";
echo "ファイル: {$filePath}\n";
echo "ファイルサイズ: " . $this->formatBytes($fileSize) . "\n";
echo "初期メモリ: " . $this->formatBytes($initialMemory) . "\n";
echo "メモリ制限: " . $this->formatBytes($this->memoryThreshold) . "\n\n";
$handle = fopen($filePath, 'r');
if (!$handle) {
throw new RuntimeException("ファイルを開けません: {$filePath}");
}
$lineCount = 0;
$results = [];
$lastMemoryReport = $initialMemory;
try {
while (($line = fgets($handle)) !== false) {
$result = $lineProcessor($line, $lineCount);
if ($result !== null) {
$results[] = $result;
}
$lineCount++;
// 1000行ごとにメモリチェック
if ($lineCount % 1000 === 0) {
$currentMemory = memory_get_peak_usage();
$memoryIncrease = $currentMemory - $lastMemoryReport;
echo "行数: {$lineCount}, " .
"現在メモリ: " . $this->formatBytes($currentMemory) . ", " .
"増加: " . $this->formatBytes($memoryIncrease) . "\n";
$lastMemoryReport = $currentMemory;
// メモリ制限チェック
if ($currentMemory > $this->memoryThreshold) {
echo "警告: メモリ使用量が制限を超えました。処理を中断します。\n";
break;
}
// 定期的なガベージコレクション
if ($lineCount % 5000 === 0) {
$this->performGarbageCollection();
}
}
}
} finally {
fclose($handle);
}
$finalMemory = memory_get_peak_usage();
echo "\n=== 処理完了 ===\n";
echo "処理行数: {$lineCount}\n";
echo "結果数: " . count($results) . "\n";
echo "最終メモリ: " . $this->formatBytes($finalMemory) . "\n";
echo "メモリ増加: " . $this->formatBytes($finalMemory - $initialMemory) . "\n";
return [
'results' => $results,
'processed_lines' => $lineCount,
'memory_usage' => [
'initial' => $initialMemory,
'final' => $finalMemory,
'peak' => $finalMemory,
'increase' => $finalMemory - $initialMemory
]
];
}
/**
* CSVファイルの効率的な処理
*/
public function processCSVFile($filePath, $hasHeader = true)
{
return $this->processLargeFile($filePath, function($line, $lineNumber) use ($hasHeader) {
// ヘッダー行をスキップ
if ($hasHeader && $lineNumber === 0) {
return null;
}
$data = str_getcsv($line);
// データの簡単な変換例
return [
'line_number' => $lineNumber,
'field_count' => count($data),
'data' => $data
];
});
}
private function performGarbageCollection()
{
$beforeGC = memory_get_usage();
if (function_exists('gc_collect_cycles')) {
$cycles = gc_collect_cycles();
$afterGC = memory_get_usage();
$freed = $beforeGC - $afterGC;
if ($freed > 0) {
echo " GC実行: " . $this->formatBytes($freed) . " 解放\n";
}
}
}
private function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
}
// 使用例
$processor = new FileProcessorWithMemoryMonitoring(64); // 64MB制限
// テスト用の大きなCSVファイルを作成
$testFile = 'large_test.csv';
$handle = fopen($testFile, 'w');
fputcsv($handle, ['ID', 'Name', 'Email', 'Data']); // ヘッダー
for ($i = 1; $i <= 50000; $i++) {
fputcsv($handle, [
$i,
"User{$i}",
"user{$i}@example.com",
str_repeat('x', 100)
]);
}
fclose($handle);
// ファイル処理実行
try {
$result = $processor->processCSVFile($testFile, true);
echo "処理結果サマリー:\n";
echo "- 処理行数: {$result['processed_lines']}\n";
echo "- 結果レコード数: " . count($result['results']) . "\n";
echo "- メモリ増加量: " .
formatBytes($result['memory_usage']['increase']) . "\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
// テストファイルを削除
if (file_exists($testFile)) {
unlink($testFile);
}
function formatBytes($size) {
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1) / 3);
return sprintf("%.2f %s", $size / pow(1024, $factor), $units[$factor]);
}
メモリ最適化のベストプラクティス
1. メモリリーク検出システム
class MemoryLeakDetector
{
private $baseline;
private $samples = [];
public function __construct()
{
$this->baseline = memory_get_peak_usage();
}
public function sample($label)
{
$current = memory_get_usage();
$peak = memory_get_peak_usage();
$this->samples[] = [
'label' => $label,
'timestamp' => microtime(true),
'current' => $current,
'peak' => $peak,
'increase_from_baseline' => $peak - $this->baseline
];
return $peak;
}
public function detectLeaks($threshold = 1048576) // 1MB
{
$potentialLeaks = [];
for ($i = 1; $i < count($this->samples); $i++) {
$current = $this->samples[$i];
$previous = $this->samples[$i-1];
$increase = $current['peak'] - $previous['peak'];
if ($increase > $threshold) {
$potentialLeaks[] = [
'from' => $previous['label'],
'to' => $current['label'],
'increase' => $increase,
'percentage' => ($increase / $previous['peak']) * 100
];
}
}
return $potentialLeaks;
}
public function getReport()
{
if (empty($this->samples)) {
return "サンプルデータがありません。";
}
$latest = end($this->samples);
$totalIncrease = $latest['peak'] - $this->baseline;
$report = "=== メモリリーク検出レポート ===\n";
$report .= "ベースライン: " . $this->formatBytes($this->baseline) . "\n";
$report .= "現在のピーク: " . $this->formatBytes($latest['peak']) . "\n";
$report .= "総増加量: " . $this->formatBytes($totalIncrease) . "\n";
$report .= "サンプル数: " . count($this->samples) . "\n\n";
$leaks = $this->detectLeaks();
if (!empty($leaks)) {
$report .= "検出されたメモリリーク:\n";
foreach ($leaks as $leak) {
$report .= sprintf(
" %s → %s: %s (%.2f%% 増加)\n",
$leak['from'],
$leak['to'],
$this->formatBytes($leak['increase']),
$leak['percentage']
);
}
} else {
$report .= "メモリリークは検出されませんでした。\n";
}
return $report;
}
private function formatBytes($size)
{
$units = ['B', 'KB', 'MB', 'GB'];
$factor = floor((strlen($size) - 1