From: Yohanes Santoso Date: 2005-10-16T12:30:09+09:00 Subject: Re: Help! define_method leaking procs... Eric Mahurin writes: > n=2**13;(1..n).each{|i| > a=(1..i).to_a;self.class.send(:define_method,:"f#{i}"){i*i}};GC.start; > IO.readlines("/proc/#{Process.pid}/status").grep(/VmSize/).display' > VmSize: 172028 kB > ruby -e ' > n=2**13;(1..n).each{|i| > a=(1..i).to_a;self.class.send(:define_method,:"f#{i}"){i*i};a=nil};GC.start; > IO.readlines("/proc/#{Process.pid}/status").grep(/VmSize/).display' > VmSize: 11504 kB Stop right there. I want to remind people that you can't use VmSize as a leak indicator. In some OS, VmSize is an always increasing number. Memory allocated in a process is not returned to the OS until the process dies. Here is a C program that free() every malloc(), yet still have outrageous VmSize; ysantoso@jenny:/tmp$ gcc -W -Wall ./leak.c -o leak && ./leak immed && ./leak not_immed Freeing immediately ----BEFORE---- malloc count = 0 free count = 0 VmSize: 1572 kB VmLck: 0 kB VmRSS: 364 kB VmData: 156 kB VmStk: 88 kB VmExe: 4 kB VmLib: 1280 kB VmPTE: 16 kB Executing leak test unwinding ----AFTER----- malloc count = 8193 free count = 8193 VmSize: 1572 kB VmLck: 0 kB VmRSS: 440 kB VmData: 156 kB VmStk: 88 kB VmExe: 4 kB VmLib: 1280 kB VmPTE: 16 kB Not freeing immediately ----BEFORE---- malloc count = 0 free count = 0 VmSize: 1568 kB VmLck: 0 kB VmRSS: 364 kB VmData: 156 kB VmStk: 84 kB VmExe: 4 kB VmLib: 1280 kB VmPTE: 16 kB Executing leak test unwinding freeing 8192 elements ----AFTER----- malloc count = 8193 free count = 8192 VmSize: 206360 kB VmLck: 0 kB VmRSS: 205280 kB VmData: 204948 kB VmStk: 84 kB VmExe: 4 kB VmLib: 1280 kB VmPTE: 216 kB ysantoso@jenny:/tmp$ cat ./leak.c #include #include #include int malloc_count = 0; int free_count = 0; void * counting_malloc(size_t size) { malloc_count++; return malloc(size); } void counting_free(void *ptr) { free_count++; if (ptr == NULL) { fprintf(stderr, "Warning: free-ing NULL pointer\n"); } free(ptr); } void display_vminfo() { FILE *status = fopen("/proc/self/status", "r"); char line[132]; while (fgets(line, 132, status)) { if (strstr(line, "Vm") == line) { printf(line); } } fclose(status); } /* allocs about 200M of mem. if free_immed_p, then memory allocated is free()ed immediately */ void leak_test(int free_immed_p) { int i, j; char **array = NULL; int max_array_idx = -1; int max_array_size = 8192; int element_size = 25*1024; array = counting_malloc(max_array_size * sizeof(char*)); if (!array) { fprintf(stderr, "Unable to malloc\n"); goto die; } for (i=0; i < max_array_size; i++) { char *element = counting_malloc(element_size); if (!element) { fprintf(stderr, "Unable to malloc\n"); goto die; } /* walk through the allocated mem to negate any lazy allocation schema */ for (j=0; j < element_size; j++) { element[j] = '\0'; } array[i] = element; max_array_idx = i; if (free_immed_p) { counting_free(element); } } die: fprintf(stderr, "unwinding\n"); if (array) { if (!free_immed_p) { fprintf(stderr, "freeing %d elements\n", max_array_idx + 1); for (i=0; i < max_array_idx; i++) { counting_free(array[i]); } } counting_free(array); } } void usage() { fprintf(stderr, "Don't understand argument. immed or not_immed\n"); } int main(int argc, char **argv) { int free_immed_p; if (argc != 2) { usage(); return 1; } if (strcmp("immed", argv[1]) == 0) { printf("Freeing immediately\n"); free_immed_p = 1; } else if (strcmp("not_immed", argv[1]) == 0) { printf("Not freeing immediately\n"); free_immed_p = 0; } else { usage(); return 1; } printf("----BEFORE----\n"); printf("malloc count = %d\n", malloc_count); printf("free count = %d\n", free_count); display_vminfo(); printf("Executing leak test\n"); leak_test(free_immed_p); printf("----AFTER-----\n"); printf("malloc count = %d\n", malloc_count); printf("free count = %d\n", free_count); display_vminfo(); return 0; } > Here is the fixed version: You can't say this if you only have VmSize. YS.