0

私は得る:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 18635837 bytes) in /Users/[...]/cake/libs/cache/file.php on line 135

そして、何がRAMを大量に消費するのか理解できません。

広範な配列とデータを含む大きな変数があります。私のコントローラーは次のように終了します:

// RENDER
$this->set(compact('var1', 'var2'));
debug(memory_get_usage()); // prints out: 33997240

33MB は 134MB には遠く及ばない

ビューの最初の行として配置debug(memory_get_usage());すると、致命的なエラーが発生します。これは、問題がビューのループにないことを意味します。コントローラーにもないようですが、コントローラーとビューの間にあります。

問題の内容を調査し、問題を解決するにはどうすればよいですか?

EDIT< 関数全体のコード:

function assignment_results($aid=null, $uid=null){
    if($aid==null){
        $this->Session->setFlash(__('Sorry but my butt got booted. 1907125790'));
        $this->redirect($this->Misc->redirectHome());
    }
    $assignment = $this->EduAssignment->getById($aid);
    // Get User IDS
    if($uid==null){
        $cus = $this->EduCourseUser->getStudentsForCourseId($assignment['EduAssignment']['edu_course_id']);
        foreach ($cus as $cu){
            $uids[]=$cu['EduCourseUser']['user_id'];
        }
    }else{
        $uids[]=$uid;
    }

    // GET WORDING
    $course = $this->EduCourse->getById($assignment['EduCourse']['id']);
    $wt = $this->WritingTranslation->getById($assignment['EduAssignment']['writing_translation_id']);
    $writing['Writing'] = $wt['Writing'];

    if($writing['Writing']['type']== 'song' || $writing['Writing']['type']== 'video')
        $this->paginate['limit'] = 2000;

    $wording = $this->paginate('Word', array('Word.writing_translation_id'=>$wt['WritingTranslation']['id']));
    $word_ids = array();
    foreach($wording as $w){
        $word_ids[]=$w['Word']['id'];
    }

    // CLICKS
    $this->Click->unbindModel(array('belongsTo' => array('Word' )));
    $clicks = $this->Click->getForAssignmentUserIds($assignment['EduAssignment']['id'], $uids);

    // Assign clicks to words
    foreach ($wording as &$wg){
        $num = 0;
        foreach ($clicks as $cl){
            if($wg['Word']['id']==$cl['Click']['word_id']){
                $num++;
            }
        }
        $wg['Word']['click_number'] = $num;
    }

    // List of words by how many times clicked:
    $wording_sorted = $wording;
    // echo(memory_get_usage());
    uasort($wording_sorted, array('TeachController', '_cmp'));
    // debug(memory_get_usage());

    // RENDER
    $this->set(compact('writing', 'wording','wording_sorted', 'assignment', 'course'));
    // debug(memory_get_usage());
}
function _cmp($a, $b){
    return $a['Word']['click_number']<$b['Word']['click_number'];
}
4

1 に答える 1

7

このエラーが発生する最も可能性の高い理由は、一度に大量のデータを処理していて、処理が完了してもデータが解放されていないためです。これは基本的に最適化の問題です。

そこに 2000 に設定された制限があることを考えると、大規模なデータベースで作業していて、その高い値が唯一の問題である可能性があると仮定しています。ただし、他にも目立ったものをいくつか強調します。

まず、データベースからさまざまな場所でデータを取得していることに注意してください。

$assignment = $this->EduAssignment->getById($aid);

$cus = $this->EduCourseUser->getStudentsForCourseId($assignment['EduAssignment']['edu_course_id']);

$course = $this->EduCourse->getById($assignment['EduCourse']['id']);

$wt = $this->WritingTranslation->getById($assignment['EduAssignment']['writing_translation_id']);

$clicks = $this->Click->getForAssignmentUserIds($assignment['EduAssignment']['id'], $uids);

これらすべてのクエリの間に、クエリのサブセットを新しい配列に保存しているため、モデル関数が実際に必要とするよりも多くのデータを返すと思われます。コードの一例を次に示します。

 foreach ($cus as $cu){
     $uids[]=$cu['EduCourseUser']['user_id'];
 }

完全に不必要と思われるもう 1 つの例を次に示し$wtます。

$writing['Writing'] = $wt['Writing'];

最後に、 を取り$wording、それを に割り当てて$wording_sorted並べ替えます。両方をビューに渡しているようですが、並べ替えられたフォームと並べ替えられていないフォームの両方がまったく同時に必要ですか? そこで何をすべきかは言えませんが、選択肢を検討してください。

この問題のいくつかを解決するためにできることは次のとおりです。

データの使用が終わったら、データへの参照を破棄する: 理由もなくメモリを占有しているため、クエリ結果を使用した後は設定を解除します。ここにカップルがあります:

// after foreach($cus as $cu) { ... }
unset($cus);

// after foreach($wording as $wd) { ... }
unset($clicks);

もちろん、他のものをビューに渡しているので、それらの設定を解除すると問題が発生します。

不要な割り当てを削除する:理由もなく別の変数が割り当てられている 1 つのインスタンスを既に強調表示しました。これをしない理由がわかりません:

// $wt = $this->WritingTranslation->getById(...)
$writing = $this->WritingTranslation->getById(...);

// change remaining references to 'wt' to 'writing'

無関係なデータをフェッチしていないことを確認してください: Cake がクエリの結果をフォーマットする方法のため、何を扱っているかを伝えるのは困難ですが、モデル内のメソッドがすべての列を1 つまたは 2 つのみが必要な場合は、テーブルとそのすべての関連付け。可能であれば、Cake モデルに渡す条件をより具体的にします。

Cake のモデルcountメソッドを使用します。すべてのクリックを取得し、ネストされたイテレータを使用して、1 か所でクリックをカウントします。これは、整数の直後には必要のない大量のデータです。別のモデル メソッドを作成することを検討してください。

// Click model
function countWordClicks($word_id) {
    return $this->find('count', array('word_id' => $word_id)) ?: 0;
}

// the following thus becomes redundant
$clicks = $this->Click->getForAssignmentUserIds($assignment['EduAssignment']['id'], $uids);

foreach ($wording as &$wg){
    $num = 0;
    foreach ($clicks as $cl){
        if($wg['Word']['id']==$cl['Click']['word_id']){
            $num++;
        }
    }
    $wg['Word']['click_number'] = $num;
}
// unset($clicks);

// and can be replaced with
foreach ($wording as &$wg) {
    $wg['Word']['click_number'] = $this->Click->countWordClicks($wg['Word']['id']);
}

(私はそれをテストすることはできませんが、正しい方向に向ける必要があります。)

これにより、より多くのデータベース クエリが導入されますが、それを最適化するためにカウンター キャッシュ フィールドが役立つ場合があります。

制限をより厳しくしてください: 制限は 2000 に設定されています。それが本当に小さいのか、それとも非常に高いのかはわかりませんが、それが1 ページあたり2000 件の結果を表示していることを意味する場合は、単純にそれを 100 件未満に減らすことで問題が解決する可能性があります他に何もしなくても問題はありません。

前述のように、これはすべて最適化のためのものであるため、微調整が必​​要なだけで、コードが間違っていたり壊れていたりすることはありません。

于 2012-04-07T14:12:26.067 に答える