肉とビールとパンケーキ by @sotarok

少し大人になった「肉とご飯と甘いもの」

PHP 5.3 でガーベジコレクションは新しくなったのか

ガベージコレクタ が追加され、デフォルトで有効になりました。

PHP: 新機能 - Manual


で、前々から噂されている通り、循環参照をうまく処理できるようになった、のか?
ということでちょっと検証してみました。

環境

どちらも、iMac上のVMWare Server 上のUbuntu(バージョんは違うけど)。メモリは512MB割り当ててる。

PHP 5.2.9
% php -v                              
PHP 5.2.9-0.dotdeb.2 with Suhosin-Patch 0.9.7 (cli) (built: Apr  7 2009 20:42:41) 
Copyright (c) 1997-2009 The PHP Group
% cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=8.10
DISTRIB_CODENAME=intrepid
DISTRIB_DESCRIPTION="Ubuntu 8.10"
PHP 5.3
% php -v
PHP 5.3.0-0.dotdeb.6 (cli) (built: Jul  3 2009 09:22:56) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies
% cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=9.04
DISTRIB_CODENAME=jaunty
DISTRIB_DESCRIPTION="Ubuntu 9.04"

検証用スクリプト

循環参照ナシ
% cat gc_no_circle.php 
<?php

ini_set("memory_limit", -1);

class Piyo
{
    public $hoge;
    function __construct()
    {
        $this->hoge = new Hoge();
    }
}

class Hoge
{
    function __construct()
    {
        $this->Fuga = new Fuga();
    }
}

class Fuga
{
    function __construct()
    {
    }
}

$test = 100000;

printf("test\n%20s MB \n", round(memory_get_usage()/1024/1024, 10));
foreach (range(0, $test) as $i) {
    $piyo = new Piyo();
    unset($piyo);
    if ($i % ($test/10) === 0) {
        printf("%10d: %15s MB \n", $i, round(memory_get_usage()/1024/1024, 10));
    }
}

printf("max\n%20s MB \n", round(memory_get_peak_usage()/1024/1024, 10));
循環参照アリ
% cat gc_circle.php 
<?php
/** 
 * 
 * 
 */

ini_set("memory_limit", -1);

class Piyo
{
    public $hoge;
    public $fuga;
    function __construct()
    {
        $this->hoge = new Hoge($this);
        $this->fuga = new Fuga($this);
    }
}

class Hoge
{
    public $piyo;
    function __construct(&$piyo)
    {
        $this->piyo = $piyo;
    }
}

class Fuga
{
    public $piyo;
    function __construct(&$piyo)
    {
        $this->piyo = $piyo;
    }
}

$test = 100000;

printf("test\n%20s MB \n", round(memory_get_usage()/1024/1024, 10));
foreach (range(0, $test) as $i) {
    $piyo = new Piyo();
    unset($piyo);
    if ($i % ($test/10) === 0) {
        printf("%10d: %15s MB \n", $i, round(memory_get_usage()/1024/1024, 10));
    }
}

printf("max\n%20s MB \n", round(memory_get_peak_usage()/1024/1024, 10));

結果

PHP 5.2.9
% php gc_no_circle.php 
test
        0.0661392212 MB 
         0:    10.104927063 MB 
     10000:     10.10496521 MB 
     20000:     10.10496521 MB 
     30000:     10.10496521 MB 
     40000:     10.10496521 MB 
     50000:     10.10496521 MB 
     60000:     10.10496521 MB 
     70000:     10.10496521 MB 
     80000:     10.10496521 MB 
     90000:     10.10496521 MB 
    100000:     10.10496521 MB 
max
         10.10521698 MB 

% php gc_circle.php 
test
        0.0684661865 MB 
         0:   10.1074295044 MB 
     10000:   19.2648048401 MB 
     20000:   28.4455986023 MB 
     30000:   38.3764038086 MB 
     40000:   46.8072090149 MB 
     50000:   58.2380065918 MB 
     60000:   66.6688117981 MB 
     70000:   75.0996170044 MB 
     80000:   83.5304222107 MB 
     90000:   97.9612159729 MB 
    100000:   106.392021179 MB 
max
       106.392383575 MB 
PHP 5.3
% php gc_no_circle.php 
test
        0.6073989868 MB 
         0:   14.5778503418 MB 
     10000:   14.5778503418 MB 
     20000:   14.5778503418 MB 
     30000:   14.5778503418 MB 
     40000:   14.5778503418 MB 
     50000:   14.5778503418 MB 
     60000:   14.5778503418 MB 
     70000:   14.5778503418 MB 
     80000:   14.5778503418 MB 
     90000:   14.5778503418 MB 
    100000:   14.5778503418 MB 
max
       14.5806121826 MB 

% php gc_circle.php 
test
         0.610710144 MB 
         0:   14.5822753906 MB 
     10000:    15.522064209 MB 
     20000:   15.5232086182 MB 
     30000:   15.5243530273 MB 
     40000:   15.5254974365 MB 
     50000:   15.5266418457 MB 
     60000:   15.5277862549 MB 
     70000:   15.5289306641 MB 
     80000:   15.5300750732 MB 
     90000:   15.5312194824 MB 
    100000:   15.5323638916 MB 
max
       19.3443527222 MB 

速度とか

PHP 5.2.9
% for ((i = 0; i < 5; i += 1)) ; do time php gc_no_circle.php >/dev/null ; done;
php gc_no_circle.php > /dev/null  0.28s user 0.06s system 100% cpu 0.337 total
php gc_no_circle.php > /dev/null  0.31s user 0.02s system 99% cpu 0.331 total
php gc_no_circle.php > /dev/null  0.31s user 0.01s system 98% cpu 0.324 total
php gc_no_circle.php > /dev/null  0.28s user 0.04s system 98% cpu 0.326 total
php gc_no_circle.php > /dev/null  0.29s user 0.04s system 100% cpu 0.328 total

% for ((i = 0; i < 5; i += 1)) ; do time php gc_circle.php >/dev/null ; done; 
php gc_circle.php > /dev/null  0.59s user 0.26s system 99% cpu 0.850 total
php gc_circle.php > /dev/null  0.60s user 0.28s system 93% cpu 0.942 total
php gc_circle.php > /dev/null  0.58s user 0.28s system 99% cpu 0.860 total
php gc_circle.php > /dev/null  0.59s user 0.25s system 99% cpu 0.841 total
php gc_circle.php > /dev/null  0.60s user 0.25s system 100% cpu 0.843 total
PHP 5.3
% for ((i = 0; i < 5; i += 1)) ; do time php gc_no_circle.php >/dev/null ; done;
php gc_no_circle.php > /dev/null  0.20s user 0.04s system 97% cpu 0.245 total
php gc_no_circle.php > /dev/null  0.20s user 0.05s system 100% cpu 0.249 total
php gc_no_circle.php > /dev/null  0.19s user 0.06s system 104% cpu 0.240 total
php gc_no_circle.php > /dev/null  0.20s user 0.05s system 99% cpu 0.251 total
php gc_no_circle.php > /dev/null  0.22s user 0.02s system 100% cpu 0.240 total

% for ((i = 0; i < 5; i += 1)) ; do time php gc_circle.php >/dev/null ; done; 
php gc_circle.php > /dev/null  0.93s user 0.03s system 100% cpu 0.956 total
php gc_circle.php > /dev/null  0.90s user 0.04s system 99% cpu 0.940 total
php gc_circle.php > /dev/null  0.90s user 0.05s system 99% cpu 0.956 total
php gc_circle.php > /dev/null  0.93s user 0.03s system 99% cpu 0.965 total
php gc_circle.php > /dev/null  0.91s user 0.05s system 99% cpu 0.967 total

結論

まあ、PHP本体のソース読んでないからそれなりに適当な考察です。

  • どうやら循環参照も適度にGCしてくれるくさい
  • でも普通にメモリ若干食ってる?(循環参照してないほう)
  • とはいえ、GCしてると速度遅くない? システムコールの時間が減ってるのかな?
  • いやまあ、こういう超単純スクリプトではあんまり参考にならないかもしれないけど。