Skip Menu |

This queue is for tickets about the DBI CPAN distribution.

Report information
The Basics
Id: 66596
Status: rejected
Priority: 0/
Queue: DBI

People
Owner: Nobody in particular
Requestors: mg.pub [...] gmx.net
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in:
  • 1.607
  • 1.609
  • 1.616
Fixed in: (no value)



Subject: Apparent memory leaks in connect() and prepare()
While trying to hunt down memory leak problems in OTRS (http://otrs.org), I stumbled over apparent memory leaks in the DBI module. Please find attached a small test script to reproduce this problem. On my system, this produces the following output: ### Checking leaks in DBI->connect() by performing 10000 calls to connect() and disconnect() The following symbol table differences were found: ARRAY changed from 636 to 10727 CODE changed from 465 to 525 GLOB changed from 779 to 1033 HASH changed from 306 to 438 REF changed from 306 to 463 REF-HASH changed from 184 to 327 SCALAR changed from 5911 to 8496 Checking leaks in DBI->prepare() by performing 10000 calls to prepare() The following symbol table differences were found: ARRAY changed from 10731 to 20732 ### This test was performed on: DBI 1.616 This is perl, v5.10.0 built for darwin-thread-multi-2level (with 2 registered patches, see perl -V for more detail) Darwin otrs-mg.fritz.box 10.6.0 Darwin Kernel Version 10.6.0: Wed Nov 10 18:13:17 PST 2010; root:xnu- 1504.9.26~3/RELEASE_I386 i386 So the outcome would be that for _every_ connect() and prepare() call, DBI causes leaked memory (additional symbol table entries). I tested this on MacOS with both DBD::mysql and DBD::Pg. If I am right, this is problematic, because long-running processes using DBI will consume constantly more memory. On another linux system, the result is less problematic, but still shows leaks in connect(): ### Checking leaks in DBI->connect() by performing 10000 calls to connect() and disconnect() The following symbol table differences were found: ARRAY changed from 696 to 792 CODE changed from 479 to 539 GLOB changed from 754 to 990 HASH changed from 286 to 426 REF changed from 259 to 416 REF-HASH changed from 133 to 276 SCALAR changed from 6415 to 9023 Checking leaks in DBI->prepare() by performing 10000 calls to prepare() The following symbol table differences were found: ### This test was performed on: DBI 1.609 (similar results with 1.616 on this system) This is perl 5, version 12, subversion 1 (v5.12.1) built for i586-linux-thread-multi Linux ub-opensuse113 2.6.34.7-0.7-default #1 SMP 2010-12-13 11:13:53 +0100 i686 i686 i386 GNU/Linux The attached test script is quite straightforward. You need a mysql database to connect to, change the connection settings as appropriate, and install Devel::Gladiator. Note that tests during the installation of this module may fail, but it still works well on my test systems. Please let me know if I can provide further information about this problem, don't hesitate to contact me at mg dot pub at gmx dot net. I would be very happy to hear about this issue, if you can confirm and also possibly fix it. With best regards, Martin Gruner
Subject: dbi_leaks.pl
#!/usr/bin/perl -w use strict; use warnings; use Devel::Gladiator (); use DBI (); =head1 SYNOPSIS Helper script to check for leaks in DBI. It seems that the DBI module in its latest version (1.616) suffers from memory leaks. This script intends to show that connect() and prepare() increase the amount of variables in the symbol table with every call. This would mean that long-running processes grow constantly. How is this achieved? We use the module Devel::Gladiator (on installation, some tests were failing on my platform MacOS, but it seems to work well!) for this purpose. It takes a 'snapsnot' of the symbol table before and after running the tests. Then these are compared and differences highlighted. =cut # You may have to adapt this to an existing database on your system. # It can be empty, without tables. my $DSN = 'DBI:mysql:database=otrs31_dev;host=localhost;'; my $DB = 'otrs31_dev'; my $PW = 'otrs31_dev'; my $ConnectCount = 10_000; print "Checking leaks in DBI->connect() by performing $ConnectCount calls to connect() and disconnect()\n"; # take snapshot of symbol table before tests my $OldSymbolTable = Devel::Gladiator::arena_ref_counts(); for (1 .. $ConnectCount) { my $DBH = DBI->connect( $DSN, $DB, $PW, { mysql_auto_reconnect => 0, }, ); $DBH->disconnect(); } print "The following symbol table differences were found:\n"; # take snapshot of symbol table after tests my $NewSymbolTable = Devel::Gladiator::arena_ref_counts(); for my $Key (sort keys %{$NewSymbolTable}) { if ($NewSymbolTable->{$Key} > (($OldSymbolTable->{$Key} || 0) + 50) ) { warn " $Key changed from $OldSymbolTable->{$Key} to $NewSymbolTable->{$Key}\n"; } } my $PrepareCount = 10_000; print "\n\nChecking leaks in DBI->prepare() by performing $PrepareCount calls to prepare()\n"; my $DBH = DBI->connect( $DSN, $DB, $PW, { mysql_auto_reconnect => 0, }, ); # take snapshot of symbol table before tests $OldSymbolTable = Devel::Gladiator::arena_ref_counts(); for (1 .. $PrepareCount) { my $STH = $DBH->prepare('SELECT 1 LIMIT 1'); # optional: these calls do not change the leak result $STH->execute(); $STH->fetchall_arrayref(); $STH->finish(); } $DBH->disconnect(); print "The following symbol table differences were found:\n"; # take snapshot of symbol table after tests $NewSymbolTable = Devel::Gladiator::arena_ref_counts(); for my $Key (sort keys %{$NewSymbolTable}) { if ($NewSymbolTable->{$Key} > (($OldSymbolTable->{$Key} || 0) + 50) ) { warn " $Key changed from $OldSymbolTable->{$Key} to $NewSymbolTable->{$Key}\n"; } } # DEBUG: print out entire information #warn Devel::Gladiator::arena_table(); # prints counts keyed by class
Does it leak with different drivers, like DBD::SQLite and DBD::CSV?
These leaks are only 'apparent'. Life is more complex than it may appear. Most of the 'leak' will be from loading the DBD::mysql driver on the first connect. My checks show no leak and, since no one else has reported a leak - which they do quickly when there actually is one - I'm going to assume that all is well and close this ticket. Thanks for the report.