[prev in list] [next in list] [prev in thread] [next in thread] 

List:       php-gtk-general
Subject:    Re: [PHP-GTK] Memory profiler for PHP?
From:       jake () lyrehc ! com
Date:       2007-06-06 1:44:30
Message-ID: 48333.24.170.161.194.1181094270.squirrel () lyrehc ! com
[Download RAW message or body]

Hello,

I don't believe there is a memory leak here, or least not a general one. 
Maybe there is something with your particular version of PHP and/or Gtk+
that's causing problems?

This is a php 5.2.3 build with the latest php-gtk from cvs.  The Gtk+ and
other libraries are all provided by the Kubuntu 7.04 (feisty fawn)
repositories.

I installed the Xdebug extension (Zend extension) using pecl.

Here is a very slightly modified version of the test case, I just added a
call count and memory information output:

---
<?php
if( !class_exists('gtk')) {
       dl('php_gtk2.so');
       ini_set('php-gtk.codepage','utf8');
}

$show_dlg_calls = 0;

class MyDialogTest extends GtkDialog {
       private $controller;

       public function __construct($parent, $title, $label) {
               parent::__construct($title, $parent, Gtk::DIALOG_MODAL |
Gtk::DIALOG_DESTROY_WITH_PARENT,
                               array('Click to Close', Gtk::RESPONSE_OK));
               $treeview = new GtkTreeView();
               $scroll = new GtkScrolledWindow();
               $scroll->add($treeview);
               $this->vbox->pack_start($scroll);

               $model = new GtkTreeStore(Gtk::TYPE_STRING);
               $treeview->set_model($model);
               $cellRenderer = new GtkCellRendererText();
               $column = new GtkTreeViewColumn('col.0', $cellRenderer,
'text', 0);
               $column->set_sort_column_id(0); //this consumes memory
               $treeview->append_column($column);

               $buttons = $this->action_area->get_children();
               $buttons[0]->grab_focus();
       }
       function __destruct() {
               echo "destroyed (derived object)\n";
               //parent::__destruct(); //if called causes an error
       }
}

$wnd = new GtkWindow();
$wnd->set_title('Memory leak');
$wnd->connect_simple('destroy', array('gtk', 'main_quit'));

$btnDlg    = new GtkButton('Show dialog');
$btnDlg->connect_simple('clicked', 'reportMem', true);
$btnDlg->connect_simple('clicked', 'showDlg');
$btnDlg->connect_simple('clicked', 'reportMem', false);

$wnd->add($btnDlg);
$wnd->show_all();

function showDlg() {
       GLOBAL $wnd, $show_dlg_calls;
        print("showDlg called ".(++$show_dlg_calls).":\n.mem:
".xdebug_memory_usage()."\npeak:
".xdebug_peak_memory_usage()."\n\n");
       $dlg = new MyDialogTest($wnd, 'My Dialog', 'click me');
       $dlg->show_all();
       $dlg->run();
       print("dialog exists: mem: ".xdebug_memory_usage()."\n");
       $dlg->destroy();
       print("post destroy(): mem: ".xdebug_memory_usage()."\n");
       unset($dlg);
       print("post unset(): mem: ".xdebug_memory_usage()."\n");
        print("showDlg done: peak: ".xdebug_peak_memory_usage()."\n\n");
}

function reportMem($pre=true)
{
    print( ($pre ? "Pre" : "Post")." function mem:
".xdebug_memory_usage()."\n");
}

gtk::main();
?>
---

Here's what happened in the beginning:

---
jake@jake-desktop:~/php$ php-gtk test.php
Pre function mem: 148272
showDlg called 1:
.mem: 148416
peak: 169656

dialog exists: mem: 151612
post destroy(): mem: 151612
destroyed (derived object)
post unset(): mem: 151612
showDlg done: peak: 169656

Post function mem: 151612
Pre function mem: 151612
showDlg called 2:
.mem: 151612
peak: 169656

dialog exists: mem: 151612
post destroy(): mem: 151612
destroyed (derived object)
post unset(): mem: 151612
showDlg done: peak: 169656

Post function mem: 151612
Pre function mem: 151612
---

Presumably the peak usage was during the initial startup.  The memory use
increases during the first call.  Interestingly, it doesn't appear to be
freed (right away?) by destroy() and unset(), but it isn't leaked either. 
For calls 3 - 9, the memory usage is stable at 151612.  Then, it increases
very slightly:

---
showDlg called 10:
.mem: 151612
peak: 169656

dialog exists: mem: 151668
post destroy(): mem: 151668
destroyed (derived object)
post unset(): mem: 151668
showDlg done: peak: 169656

Post function mem: 151668
Pre function mem: 151668
showDlg called 11:
.mem: 151668
peak: 169656

dialog exists: mem: 151668
post destroy(): mem: 151668
destroyed (derived object)
post unset(): mem: 151668
showDlg done: peak: 169656
---

I'm not sure what this tiny (56 byte) increase is caused by, but its not
even close to on the order of the increase from the initial call.  I
clicked away a little more and it never increased again, here's the final
output before I stopped:

---
showDlg called 141:
.mem: 151668
peak: 169656

dialog exists: mem: 151668
post destroy(): mem: 151668
destroyed (derived object)
post unset(): mem: 151668
showDlg done: peak: 169656

Post function mem: 151668
---

Hopefully some future tests will give more insight into how memory is
handled between PHP and Gtk.  It seems fairly clear the memory is
remaining allocated and being reused in this case.  I watched top as well
in case the Xdebug output is misleading and it only increased when the
memory reported by Xdebug increased.

However, in my own application I added memory tracking as well and I do
see it being freed.  I consume much larger amounts of memory than a simple
test like this.  First, I do see memory usage drop as it gets into a code
section where it starts using unset() for images that are no longer
needed.  Second, when the method ends and all the variables that are not
explicitly unset() go out of scope I see a very large decrease in memory
usage; this is what prompted me to report pre and post function call usage
in the test.

Unfortunately, this doesn't make it clear to me how to reliably tell from
code examination (as opposed to trial-and-error) if-and-when particular
memory will be freed.  It might not even be possible to know for sure; for
example, in Java you can call the garbage collector but this is really
more of a suggestion that it be run and won't ensure it is actually run at
the time.

I'll try to think up some test cases that do more investigation into how
various factors affect memory handling to report to the list.  If anyone
has a particular case/aspect they'd like to see, drop me a note or post to
the list.

-Jake Cobb

>> Now the real test:
>> I create a dialog and after I call destroy() manually and then I unset the
>> object.
>> I do this repeatedly and memory usage keeps increasing.
>> So neither destroy() frees the memory.
>>
>> There is propably some refference of some object which is not dying.
>> How can I ensure that there are no child widgets somewhere in my hierarchy
>> that are still refferenced ?
>>
>Got it, I removed everything until I've seen what it is.
>But it seems like a memory leak of
>GtkTreeViewColumn::set_sort_column_id()
>rather than a GtkDialog bug. GtkDialog itself and it's subclass does not
>seam to leak memory.
>
>Here is a minimal code to produce the leak. If I reopen the dialog
>continously I notice about 100kB of memory increasing for every 20-30
>dialogs. Commenting out the $column->set_sort_column_id(0) solves the
>problem.
>
>#!/usr/bin/php
><?php
>if( !class_exists('gtk')) {
>       dl('php_gtk2.so');
>       ini_set('php-gtk.codepage','utf8');
>}

>class MyDialogTest extends GtkDialog {
>       private $controller;

>       public function __construct($parent, $title, $label) {
>               parent::__construct($title, $parent, Gtk::DIALOG_MODAL |
Gtk::DIALOG_DESTROY_WITH_PARENT,
>                               array('Click to Close', Gtk::RESPONSE_OK));
>               $treeview = new GtkTreeView();
>               $scroll = new GtkScrolledWindow();
>               $scroll->add($treeview);
>               $this->vbox->pack_start($scroll);
>
>               $model = new GtkTreeStore(Gtk::TYPE_STRING);
>               $treeview->set_model($model);
>               $cellRenderer = new GtkCellRendererText();
>               $column = new GtkTreeViewColumn('col.0', $cellRenderer,
>'text', 0);
>               $column->set_sort_column_id(0); //this consumes memory
>               $treeview->append_column($column);
>
>               $buttons = $this->action_area->get_children();
>               $buttons[0]->grab_focus();
>       }
>       function __destruct() {
>               echo "destroyed (derived object)\n";
>               //parent::__destruct(); //if called causes an error
>       }
>}
>
>$wnd = new GtkWindow();
>$wnd->set_title('Memory leak');
>$wnd->connect_simple('destroy', array('gtk', 'main_quit'));
>
>$btnDlg    = new GtkButton('Show dialog');
>$btnDlg->connect_simple('clicked', 'showDlg');
>
>$wnd->add($btnDlg);
>$wnd->show_all();
>
>function showDlg() {
>       GLOBAL $wnd;
>      $dlg = new MyDialogTest($wnd, 'My Dialog', 'click me');
>       $dlg->show_all();
>       $dlg->run();
>       $dlg->destroy();
>       unset($dlg);
>}
>
>gtk::main();
>
>?>
>
>--
>PHP-GTK General Mailing List (http://gtk.php.net/)
>To unsubscribe, visit: http://www.php.net/unsub.php

-- 
PHP-GTK General Mailing List (http://gtk.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic