|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2012-05-22 20:05 UTC] hosiplan at gmail dot com
Description:
------------
When PDO is told to use my row class and pass PDOStatement into it, it creates
cyclic reference, that prevents GC from deleting the row data, when not required
anymore.
Test script:
---------------
$db = new PDO('mysql:host=127.0.0.1;dbname=information_schema', 'root', 'password');
class DbRow { public function __construct($stt = NULL) { } }
$begin = memory_get_usage();
for ($i=0; $i < 10 ;$i++) {
$stt = $db->prepare("SELECT * FROM COLLATIONS");
$stt->setFetchMode(PDO::FETCH_CLASS, 'DbRow');
$stt->execute();
$rows = $stt->fetchAll();
echo number_format((memory_get_usage() - $begin) / 1000000, 2, '.', ' '), " MB\n";
}
Expected result:
----------------
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
0.05 MB
Actual result:
--------------
0.00 MB
0.05 MB
0.10 MB
0.14 MB
0.19 MB
0.24 MB
0.29 MB
0.34 MB
0.38 MB
0.43 MB
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 09:00:01 2025 UTC |
Sorry, I've coppied wrong code Test script: --------------- $db = new PDO('mysql:host=127.0.0.1;dbname=information_schema', 'root', 'password'); class DbRow { public function __construct($stt = NULL) { } } $begin = memory_get_usage(); for ($i=0; $i < 10 ;$i++) { $stt = $db->prepare("SELECT * FROM COLLATIONS"); $stt->setFetchMode(PDO::FETCH_CLASS, 'DbRow', array($stt)); $stt->execute(); $rows = $stt->fetchAll(); echo number_format((memory_get_usage() - $begin) / 1000000, 2, '.', ' '), " MB\n"; }It matters a whole lot what the cause is. It is not a leak. First, it helps to have some clean test code, such as this: class pdo_row { public function __construct($stmt) { $stmt = NULL; } } /* $pdo = new PDO("mysql:dbname=test;unix_socket=/var/run/mysql/mysql.sock", "root", ""); */ $pdo = new PDO("sqlite::memory:"); $pdo->exec("DROP TABLE test"); $pdo->exec("CREATE TABLE test(id INT, col VARCHAR(200))"); for ($i = 0; $i < 100; $i++) { $pdo->exec(sprintf("INSERT INTO test(id, col) VALUES (1, '012345678901234567890123456789012345678901234567890123456789-%d')", $i)); } for ($i = 0; $i < 10; $i++) { $stmt = $pdo->prepare("SELECT col FROM test"); $stmt->execute(); $stmt->setFetchMode(PDO::FETCH_CLASS, 'pdo_row', array($stmt)); $rows = $stmt->fetchAll(); printf("emalloc %d kB, malloc %d kB\n", memory_get_usage() / 1024, memory_get_usage(true) / 1024); } Running the above for SQLlite gives me: emalloc 205 kB, malloc 256 kB emalloc 206 kB, malloc 512 kB emalloc 207 kB, malloc 512 kB emalloc 208 kB, malloc 512 kB emalloc 209 kB, malloc 512 kB emalloc 210 kB, malloc 512 kB emalloc 211 kB, malloc 512 kB emalloc 212 kB, malloc 512 kB emalloc 213 kB, malloc 512 kB emalloc 214 kB, malloc 512 kB Running the above for MySQL gives me: emalloc 221 kB, malloc 256 kB emalloc 231 kB, malloc 512 kB emalloc 240 kB, malloc 512 kB emalloc 250 kB, malloc 512 kB emalloc 259 kB, malloc 512 kB emalloc 269 kB, malloc 512 kB emalloc 278 kB, malloc 512 kB emalloc 288 kB, malloc 512 kB emalloc 297 kB, malloc 512 kB emalloc 307 kB, malloc 512 kB Second, it helps to understand the matter. In both cases emalloc() figures increase, which is to be expected. PHP objects are created, the extensions allocate memory using the PHP internal e*alloc() function family and the figures increase. And, in both cases malloc() figures are the same. No difference between MySQL and SQLite.Try this one instead. It does not matter whether you use MySQL or SQLlite. The problem is that if you add some arguments for the constructor, it consumes more and more memory without any reason. --------- class pdo_row { public function __construct() { } } $pdo = new PDO('mysql:host=127.0.0.1;dbname=information_schema', 'root', ''); $pdo->exec("DROP TABLE test"); $pdo->exec("CREATE TABLE test(id INT, col VARCHAR(200))"); for ($i = 0; $i < 100; $i++) { $pdo->exec(sprintf("INSERT INTO test(id, col) VALUES (1, '012345678901234567890123456789012345678901234567890123456789-%d')", $i)); } printf("With ctor argument (memory usage increase):"); for ($i = 0; $i < 10; $i++) { $stmt = $pdo->prepare("SELECT col FROM test"); $stmt->setFetchMode(PDO::FETCH_CLASS, 'pdo_row', array($stmt)); $stmt->execute(); $rows = $stmt->fetchAll(); printf("emalloc %d kB, malloc %d kB\n", memory_get_usage() / 1024, memory_get_usage(true) / 1024); } printf("Without ctor argument (no memory usage increase):"); for ($i = 0; $i < 10; $i++) { $stmt = $pdo->prepare("SELECT col FROM test"); $stmt->setFetchMode(PDO::FETCH_CLASS, 'pdo_row'); $stmt->execute(); $rows = $stmt->fetchAll(); printf("emalloc %d kB, malloc %d kB\n", memory_get_usage() / 1024, memory_get_usage(true) / 1024); }