We have discovered a memory leak problem using FreeBSD 2.2.8, g++ version 2.8.1, and GNU STL libraries (libstdc++) version 2.8.1.1. We are also using the gcc28 patches provided by the FreeBSD web-site. The problem appears when you have two C++ files sharing an STL list, set / multiset or map / multimap. There is actually a larger problem with static variables in template classes, but the STL objects are the easiest way to produce this problem. One of the files inserts entries into the list (causing memory allocation), while the other file releases the entries from the list (returning the memory to an STL allocator). Examining the assembler file created for the two objects, we saw ".weak" definitions for the STL allocator's free_list (among many other weak definitions), which we would expect the linker to combine into a single actual location in the final executable. Even running "nm" on the final executable only shows one free_list variable. However, using gdb to step through the code, and examining where the allocation and deallocation routines were actually accessing the free_list showed that the linker had actually kept each free_list, and all references to each was kept local within each object file. This caused one STL allocator to continually go to the OS to get memory to add to the list, while another STL allocator was releasing those blocks back into its own private buffer. Fix: We were able to work around this problem by patching the gcc28 patches. We removed the following patch from "patch-01", which has the effect of disabling gcc's use of weak variable declarations : + +#define ASM_WEAKEN_LABEL(FILE,NAME) \ + do { fputs ("\t.weak\t", FILE); assemble_name (FILE, NAME); \ + fputc ('\n', FILE); } while (0) + After applying this patch and recompiling, only one free_list exists in the executable, and the memory leak goes away. How-To-Repeat: The following code can be used to produce the problem. 1. Compile it using "g++ -o leak *.C" 2. Start "top" in a separate window. 3. Run "leak", and watch the size of the process grow through each iteration. -- file : header.H -- #ifndef __header_H #define __header_H #include <list> typedef list<int> IntList; void producer(IntList &l); void consumer(); #endif -- file : consumer.C -- #include "header.H" #include <unistd.h> #include <stdio.h> main() { for (int i = 0; i < 20; i++) { IntList l; producer(l); l.clear(); printf("Done with pass %d\n", i+1); sleep(1); } } -- file : producer.C -- #include "header.H" // Simply adds list_size entries to the list. The caller will release them void producer(IntList &l) { for (int i = 0; i < 100000; i++) { l.push_back(1); } }
Responsible Changed From-To: freebsd-ports->obrien Over to maintainer
I am not sure what to do about this... some software depends on weak symbol support. :-( -- -- David (obrien@NUXI.com)
State Changed From-To: open->closed Commited.