PHPでメモリを解放する際の優れた点:unset()または$ var = null


244

2つ目は関数呼び出しのオーバーヘッドを回避することを理解しています(updateは実際には言語構造です)が、一方が他方よりも優れているかどうかを知ることは興味深いでしょう。私はunset()自分のコーディングのほとんどに使用していますが、$var = null代わりに、ネットから見つけたいくつかの立派なクラスを調べました。

好ましいものはありますか、そしてその理由は何ですか?

回答:


234

それは2009年にunsetマニュアルのページで言及されました:

unset()その名前のとおりに実行します-変数の設定を解除します。メモリの即時解放は強制されません。PHPのガベージコレクターは、適切だと判断したときにそれを実行します。意図的に、これらのCPUサイクルが不要になったとき、または最初に発生するスクリプトがメモリ不足になる前に遅くなります。

実行している$whatever = null;場合は、変数のデータを書き換えています。あなたはより速くメモリを解放/縮小するかもしれませんが、それは本当にそれらをより早く必要とするコードからCPUサイクルを奪い、結果として全体的な実行時間をより長くするかもしれません。

(2013年以降、そのunsetmanページにはそのセクションが含まれなくなりました)

php5.3までは、2つのオブジェクトが循環参照(親子関係など)している場合、親オブジェクトでunset()を呼び出しても、子オブジェクトの親参照に使用されていたメモリは解放されません。(または、親オブジェクトがガベージコレクションされたときにメモリが解放されることもありません。)(バグ33595


unsetと= nullの違い」という質問は、いくつかの違いを詳しく説明しています。


unset($a)$aシンボルテーブルからも削除します。例えば:

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

出力:

Notice: Undefined variable: a in xxx
NULL

しかし、いつ$a = null使用されるか:

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:

NULL

それはそれ$a = nullよりも少し速いようですunset():シンボルテーブルエントリの更新はそれを削除するより速いようです。


  • 存在しない(unset)変数と、エラーがトリガーされ、変数式の値はnullになります。(なぜなら、PHPは他に何をすべきか?すべての式が何らかの値をもたらす必要があるからです。)
  • ただし、nullが割り当てられている変数は、完全に通常の変数です。

18
$whateverオブジェクトを指す場合$whatever = null、オブジェクト自体ではなくポインタを上書きするため、基本的にはと同じように動作しunset()ます。
Gras Double

1
@VonC:参照しているphp.netの未設定の引用はもう存在しません。
ユルゲンThelen

@JürgenThelenは本当ですが、その古い答えの内容はまだ関連性があるようです、違いますか?
VonC 2013年

1
@VonC:もちろんです。「CPUサイクルは不要」と「メモリ不足」がガベージコレクションをトリガーする前に、私はよくわかりません。stackoverflow.com/q/20230626/693207を参照してください。多分あなたはいくつかの光を当てることができますか?
ユルゲンThelen

1
@Omar回答を編集しました。2009からのunset manページ(2009バージョンにリンクしています)には、同じページの現在のバージョンには存在しないセクションが含まれています。
VonC、2016

48

unset実際には関数ではありませんが、言語構造です。これは、a returnまたはにすぎませんinclude

パフォーマンスの問題は別として、を使用unsetすると、コードの意図がより明確になります。


それが私がいつもそれらを使っていた理由です、個人的に私はそれらが$ var = nullよりもよく見えると思いました。ちなみに、私は常にNULLフルキャップを使用してきましたが、なぜ今なのかわかりません。
アレックス

1
@VonC:ええ、私はそれを考えましたが、なぜ小文字のtrue、false、nullを使用できるのですか?
アレックス

3
@ alex、unsetでそれを行うことができます。例:「$ test = 4;(unset)$ test;」-奇妙ですが真であり、設定を解除する前に$ testの値を返します。いずれにせよ、PHPマニュアルでは、それが言語構造であることを確認しています。
thomasrutter 2009年

5
@alex:PSR-2 では、すべてのキーワードに小文字が必要です。
Tgr

2
@alex-PHPキーワードは大文字と小文字を区別しません。あなたももたらすかもしれないunsetとしてUnSeT、たとえば、。コミュニティーは、スタイルの問題としてすべて小文字で解決していますが、他の大文字小文字は引き続き機能します。
マークリード

35

変数に対してunset()を実行すると、基本的に変数に「ガベージコレクション」のマークが付けられます(PHPには実際には1つはありませんが、たとえば)。これにより、メモリがすぐに使用できなくなります。変数にはデータが格納されなくなりましたが、スタックはより大きなサイズのままです。nullメソッドを実行すると、データが削除され、スタックメモリがすぐに縮小されます。

これは、個人的な経験や他の人たちからのものです。ここで unset()関数のコメントを参照してください。

個人的には、ループの反復間でunset()を使用して、スタックのサイズがヨーヨーになるのを遅らせる必要がないようにしています。データはなくなりましたが、フットプリントは残っています。次の反復では、メモリはすでにphpによって使用されているため、次の変数をより早く初期化できます。


15
値をNULLに保持するために必要なメモリが、以前に保持していた値を保持するために必要なメモリよりも少ない場合は、何かをNULLに設定するとメリットがあります。たとえば、長い文字列です。文字列が定数ではなく、その参照カウントがゼロに低下した場合、そのメモリを解放する必要があります。Unsetはよりクリーンです-参照を維持しなくなりました。ガベージコレクションを待つ必要はありますが、メモリが不足するとガベージコレクションがトリガーされるため、メモリを占有していないものとして扱うのが安全です。
thomasrutter 2009年

両方使用できませんか?nullに等しく、次に設定解除しますか?
Nabeel Khan、2016

2
@NabeelKhanループ内でunset()を使用し、ループを終了するときにそれを無効にすることをお勧めします。それ以外の場合、ループ内で両方を実行するとパフォーマンスに影響があります。ループを使用していない場合は、舞台裏でunset()ロジックをすでに実行しているため、無効にします。
ウィリアムホロイド2016

27
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";
?>

「= null」の方が速いようです。

PHP 5.4の結果:

  • 0.88389301300049秒かかりました
  • 2.1757180690765秒かかりました

PHP 5.3の結果:

  • 1.7235369682312秒かかりました
  • 2.9490959644318秒かかりました

PHP 5.2の結果:

  • 3.0069220066071秒かかりました
  • 4.7002630233765秒かかりました

PHP 5.1の結果:

  • 2.6272349357605秒かかりました
  • 5.0403649806976秒かかりました

PHP 5.0と4.4では物事が異なって見え始めます。

5.0:

  • 10.038941144943秒かかりました
  • 7.0874409675598秒かかりました

4.4:

  • 7.5352551937103秒かかりました
  • 6.6245851516724秒かかりました

PHP 4.4ではmicrotime(true)は機能しないので、php.net / microtime /例1にあるmicrotime_floatの例を使用する必要があることに注意してください。


7
あなたのテストには欠陥があると思います。最初のループは単純な再割り当てで、2番目のループは同じシンボルを破棄して再作成します。テストが配列でやり直された場合unsetはより高速です。私は後でunsetケースの存在をチェックするテストがあります。そのテストでnullは、それを設定するとわずかに速くなります。テスト:pastebin.com/fUe57C51
Knyri

4
@ansur、gc_collect_cyclesより正確な結果を得るために、常にタイマーを開始する前に呼び出します。
Pacerier 2013

@knyriリンクして頂けますか?
Nabeel Khan、2016

@NabeelKhanそのテストの結果はもうありません。以前のコメントにテストコードへのリンクがあります。
Knyri

19

それは配列要素と違いを生みます。

この例を考えてみましょう

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

ここでは、キー「テスト」がまだ存在しています。ただし、この例では

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

キーは存在しません。


18

参照によってコピーされた変数に対しては、別の方法で機能します。

$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5

$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null

5
私は数年前にphpをコーディングしていて、元のvarを参照することについて「&」を見たことはありません。ありがとう+ 1 :)
Chris

1
$ a = 78; $ b = $ a; unset($ a); var_dump($ b); // 78; var_dump($ a); //未定義の変数:a
zloctb

13

オブジェクトに関して、特にレイジーロードのシナリオでは、ガベージコレクターがアイドルCPUサイクルで実行されていることを考慮する必要があります。そのため、多くのオブジェクトが短い時間でロードされているときに問題が発生すると想定すると、メモリの解放が解決されます。

GCがメモリを収集できるようにするには、time_nanosleepを使用します。変数をnullに設定することをお勧めします。

本番サーバーでテストしましたが、最初はジョブが50MBを消費し、その後停止しました。nanosleepが使用された後、14MBは一定のメモリ消費でした。

これは、PHPのバージョンによって異なるGCの動作に依存すると言うべきです。しかし、PHP 5.3では問題なく動作します。

例えば。このサンプル(VirtueMart2 googleフィードから取得したコード)

for($n=0; $n<count($ids); $n++)
{
    //unset($product); //usefull for arrays
    $product = null
    if( $n % 50 == 0 )
    {
        // let GC do the memory job
        //echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
        time_nanosleep(0, 10000000);
    }

    $product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
    ...

3

まだ疑っていますが、スクリプトで試しましたが、xdebugを使用して、アプリのメモリ使用量にどのように影響するかを確認しています。スクリプトは次のように私の関数に設定されています:

function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
    $sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
    if($showSql === FALSE) {
        $sql = mysql_query($sql) or die(mysql_error());
        $data = mysql_fetch_array($sql);
        return $data[0];
    } else echo $sql;
}

そして、returnコードの直前にunsetを追加すると、160200が得られます。次に、それを変更しようとすると、$sql = NULL160224が得られます:)

しかし、私がunset()またはNULLを使用していない場合、この比較にはユニークなものがあります。xdebugはメモリ使用量として160144を与えます

したがって、unset()またはNULLを使用する行を指定すると、アプリケーションにプロセスが追加され、コードの原点を維持し、使用している変数をできるだけ効果的に減らす方が良いと思います。

私が間違っていたら訂正してください、ありがとう


$ data [0]アイテムを返している間、配列全体が参照されていると思いますが、これは単なる仮説です。$ data [0]をローカル変数にコピーし、配列をnullに設定して、ローカル変数を返します。良い背景はここtuxradar.com/practicalphp/18/1/11とofcです。php.net/manual/en/features.gc.php
OSP

2

私はのための新しいパフォーマンス・テストを作成unsetし、=nullコメントで述べたように、ここで記述されたが、エラー(要素の再作成を)持っているので、。今は問題ではないように、私は配列を使用しました。

<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = 'a';
    $arr2[$i] = 'a';
}

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = null;
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

しかし、私はPHP 5.5.9サーバーでのみテストできます、ここでは結果:-4.4571571350098秒かかりました-4.4425978660583秒かかりました

unsetは読みやすさの理由で好みます。


2

PHP 7は、このようなメモリ管理の問題と、その使用量を最小限に抑えられるようにすでに取り組んでいます。

<?php
  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
     $a = 'a';
     unset($a);
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

?>

PHP 7.1出力:

0.16778993606567秒かかりました0.16630101203918秒かかりました


1

unset即時メモリを解放しない場合でもコードは非常に役立ち、メソッドを終了する前にコードステップを渡すたびにこれを行うことをお勧めします。即時メモリの解放についてではないことに注意してください。即時メモリはCPU用で、RAMであるセカンダリメモリはどうでしょうか。

また、これはメモリリークの防止にも取り組みます。

このリンクを参照してください http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

私は長い間unsetを使用しています。

すでに配列として使用されているすべての変数の設定を常に解除するために、コードでこのようなより良い実践をしてください。

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

そしてjust unset($data);、すべての変数の使用を解放します。

設定を解除するには、関連トピックを参照してください

PHPで変数を設定解除することはどのくらい重要ですか?

[バグ]


1

記録のために、そしてそれがかかる時間を除いて:

<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n";

echo "<hr>function:<br>";
function test() {
    $x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

戻る

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

結論、期待どおりのnullと未設定の両方の空きメモリ(実行の終了時だけでなく)。また、変数を再割り当てすると、ある時点で値が2回保持されます(520216対438352)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.