If a dict is created and filled via "dict incr" with a bigger set of keys it seems to leak. If the dict is destroyed (unset or overwritten) a lot of memory still keeps allocated.
The following code consumes approx. 500MB memory on my machine (Linux, 64-bit):
set chars {a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9}
for {set i 0} {$i < 1000} {incr i} {
append prefix [lindex $chars [expr int([tcl::mathfunc::rand]*62)]]
set dictionary [dict create]
foreach char $chars {
foreach c $chars {
dict incr dictionary $prefix$char$c $i
}
}
}
I've tested Tcl 8.5.2 (OpenSuSE 11.0, x86_64), 8.5.7, 8.6b1 (both built from source),
all three leak, cf. attached valgrind log files for same code but only 100 cycles.
For 8.5.2 TclBN_mp_init_size according to valgrind seems to be reason.
% uname -mrvs
Linux 2.6.25.20-0.5-default #1 SMP 2009-08-14 01:48:11 +0200 x86_64
valgrind log files for
May the patch below fix the problem?
--- ./generic/tclDictObj.c~ 2009-01-06 17:07:17.000000000 +0100
+++ ./generic/tclDictObj.c 2009-10-08 15:00:29.000000000 +0200
@@ -2148,6 +2148,7 @@
mp_int increment;
code = Tcl_GetBignumFromObj(interp, objv[3], &increment);
+ mp_clear(&increment);
if (code != TCL_OK) {
Tcl_AddErrorInfo(interp, "\n (reading increment)");
} else {
Leaks 8 words per *interpreted* [dict incr] that creates a dictionary entry.
And requires an explicit increment to do so.
The following test seems to be exact:
test dict-19.3 {dict: testing for leaks - Bug 2874678} -setup {
proc memtest script {
set end [lindex [split [memory info] \n] 3 3]
for {set i 0} {$i < 5} {incr i} {
uplevel 1 $script
set tmp $end
set end [lindex [split [memory info] \n] 3 3]
}
expr {$end - $tmp}
}
} -constraints memory -body {
set d aDictVar; # Force interpreted [dict incr]
memtest {
dict incr $d aKey 0
unset $d
}
} -cleanup {
unset d
rename memtest {}
} -result 0
Fixed in HEAD and 8.5 (the patch was slightly wrong, but helped narrow down what needed to be done a lot; thanks!)