5

この単純なスクリプトを実行すると、以下に投稿された出力が得られます。コードまたは Zend Framework/Magento スタックのいずれかにメモリ リークがあると思われます。この問題は、あらゆる種類の Magento コレクションを反復するときに発生します。私が行方不明または間違っていることはありますか?

脚本:

$customersCollection = Mage::getModel('customer/customer')->getCollection();

foreach($customersCollection as $customer) {
  $customer->load();
  $customer = null;
  echo memory_get_usage(). "\n";
}

出力:

102389104
102392920
...
110542528
110544744
4

5 に答える 5

3

オブジェクト (またはその他の値) のメモリは、PHP プロセスのどこにも参照がない場合にのみ解放できます。あなたの場合、行$customer = nullはそのオブジェクトへの参照の数を1つ減らすだけですが、ゼロにはなりません。

より単純なループを考えると、これはより明確になる可能性があります。

$test = array('a' => 'hello');
foreach ( $test as $key => $value )
{
    // $value points at the same memory location as $test['a']
    // internally, that "zval" has a "refcount" of 2

    $value = null;
    // $value now points to a new memory location, but $test['a'] is unnaffected
    // the refcount drops to 1, but no memory is freed
}

オブジェクトを使用しているため、追加のひねりがあります。新しいコピーを作成せずに、ループ内のオブジェクトを変更できます。

$test = array('a' => new __stdClass);
// $test['a'] is an empty object

foreach ( $test as $key => $value )
{
    // $value points at the same object as $test['a']
    // internally, that object has a "refcount" of 2

    $value->foo = "Some data that wasn't there before";
    // $value is still the same object as $test['a'], but that object now has extra data
    // This requires additional memory to store that object

    $value = null;
    // $value now points to a new memory location, but $test['a'] is unnaffected
    // the refcount drops to 1, but no memory is freed
}

// $test['a']->foo now contains the string assigned in the loop, consuming extra memory

あなたの場合、->load()メソッドはおそらく各メンバーのデータ量を$customersCollection順番に拡大しているため、それぞれにより多くのメモリが必要です。$customersCollectionループの前後を調べると、おそらくこれを確認できます。

于 2013-04-13T16:17:27.850 に答える
0

まず、変数の設定を解除するときは、$variable=null の代わりに unset($variable) を使用します。基本的に同じことを行いますが、意図に関してははるかに明確です。

第 2 に、PHP は死ぬことを意図しています。メモリ リークは大きな問題ではありません。PHP リクエストはおそらく数秒続き、その後プロセスが停止し、使用していたすべてのメモリが次のリクエストのために解放されます。スケーリングの問題が発生していない限り、心配する必要はありません。

編集: コードの品質を心配する必要はありませんが、このような場合、問題が発生していない限り、それを防止しようとする価値はほとんどありません。

于 2013-04-13T16:53:46.003 に答える
0

メモリ リークを処理する他の方法は、ループ内で exec を呼び出し、その exec 関数にメモリ リークを引き起こすジョブ部分を実行させることです。

したがって、その部分が完了し、その exec 内のすべてのメモリ リークが終了すると、解放されます。

したがって、大量の反復では、そうでなければ追加し続けるこのメモリ損失が処理されます。

于 2013-04-13T19:41:36.780 に答える
0

ループ内で load() を呼び出すのは非常にコストのかかる呼び出しであるため、@bemarks の応答はここでは正しいアプローチです。

$customer->load() を呼び出すと、$customersCollection によって参照されるメモリが増分的に割り当てられ、そのメモリはループの最後まで解放されません。

ただし、何らかの理由で load() を呼び出す必要がある場合、GC は各反復でモデルによって割り当てられたすべてのメモリを解放するため、以下のコードはメモリ リークしません。

$customersCollection = Mage::getModel('customer/customer')->getCollection();

foreach($customersCollection as $customer) {
    $customerCopy = Mage::getModel('customer/customer')->load($customer->getId());

    //Call to $customerCopy methods

    echo memory_get_usage(). "\n";
}
于 2013-04-15T17:14:13.747 に答える