c++memory-leaksvalgrindlocaltimemassif

localtime_r consuming some memory before program exit


I use valgrind's massif to track memory usage at the last stage before program exit and found

which is calling localtime_r and consuming some memory.

16 ComputeLocalTime(time_t local, struct tm *ptm)
17 {
18 #ifdef HAVE_LOCALTIME_R
19     return localtime_r(&local, ptm);
20 #else
21     struct tm *otm = localtime(&local);
22     if (!otm)

ms_print of last snapshot from valgrind's massif

427711 --------------------------------------------------------------------------------
427712   n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
427713 --------------------------------------------------------------------------------
427714  95 15,049,552,789              256              165            91            0
427715 64.45% (165B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
427716 ->36.72% (94B) 0x37AFA9EA6A: __tzfile_read (in /lib64/libc-2.12.so)
427717 | ->36.72% (94B) 0x37AFA9DC02: tzset_internal (in /lib64/libc-2.12.so)
427718 |   ->36.72% (94B) 0x37AFA9DD67: __tz_convert (in /lib64/libc-2.12.so)
427719 |     ->36.72% (94B) 0x4CAE552: js::DateTimeInfo::updateTimeZoneAdjustment() (DateTime.cpp:19)
427720 |       ->36.72% (94B) 0x4D814C9: JSRuntime::JSRuntime(JSUseHelperThreads) (jsapi.cpp:856)
427721 |         ->36.72% (94B) 0x4D8B71A: JS_NewRuntime(unsigned int, JSUseHelperThreads) (Utility.h:491)
427722 |           ->36.72% (94B) 0x40162A: main (js.cc:58)
427723 |
427724 ->15.62% (40B) 0x37AFA9D0D0: __tzstring (in /lib64/libc-2.12.so)
427725 | ->15.62% (40B) 0x37AFA9EF99: __tzfile_read (in /lib64/libc-2.12.so)
427726 |   ->15.62% (40B) 0x37AFA9DC02: tzset_internal (in /lib64/libc-2.12.so)
427727 |     ->15.62% (40B) 0x37AFA9DD67: __tz_convert (in /lib64/libc-2.12.so)
427728 |       ->15.62% (40B) 0x4CAE552: js::DateTimeInfo::updateTimeZoneAdjustment() (DateTime.cpp:19)
427729 |         ->15.62% (40B) 0x4D814C9: JSRuntime::JSRuntime(JSUseHelperThreads) (jsapi.cpp:856)
427730 |           ->15.62% (40B) 0x4D8B71A: JS_NewRuntime(unsigned int, JSUseHelperThreads) (Utility.h:491)
427731 |             ->15.62% (40B) 0x40162A: main (js.cc:58)
427732 |
427733 ->05.86% (15B) 0x37AFA81170: strdup (in /lib64/libc-2.12.so)
427734 | ->05.86% (15B) 0x37AFA9DBEF: tzset_internal (in /lib64/libc-2.12.so)
427735 |   ->05.86% (15B) 0x37AFA9DD67: __tz_convert (in /lib64/libc-2.12.so)
427736 |     ->05.86% (15B) 0x4CAE552: js::DateTimeInfo::updateTimeZoneAdjustment() (DateTime.cpp:19)
427737 |       ->05.86% (15B) 0x4D814C9: JSRuntime::JSRuntime(JSUseHelperThreads) (jsapi.cpp:856)
427738 |         ->05.86% (15B) 0x4D8B71A: JS_NewRuntime(unsigned int, JSUseHelperThreads) (Utility.h:491)
427739 |           ->05.86% (15B) 0x40162A: main (js.cc:58)
427740 |
427741 ->03.12% (8B) 0x4015C6: allocate() (js.cc:41)
427742 | ->03.12% (8B) 0x40187E: main (js.cc:114)
427743 |
427744 ->03.12% (8B) 0x4015E2: allocate() (js.cc:43)
427745 | ->03.12% (8B) 0x40187E: main (js.cc:114)
427746 |
427747 ->00.00% (0B) 0x4D8B3E8: JSRuntime::init(unsigned int) (Utility.h:154)
427748 | ->00.00% (0B) 0x4D8B73B: JS_NewRuntime(unsigned int, JSUseHelperThreads) (jsapi.cpp:1121)
427749 |   ->00.00% (0B) 0x40162A: main (js.cc:58)
427750 |
427751 ->00.00% (0B) 0x4D8B435: JSRuntime::init(unsigned int) (Utility.h:154)
427752 | ->00.00% (0B) 0x4D8B73B: JS_NewRuntime(unsigned int, JSUseHelperThreads) (jsapi.cpp:1121)
427753 |   ->00.00% (0B) 0x40162A: main (js.cc:58)

Is there anyway to free this before my program exit ? ( from my understanding it will be cleared when program exit )


Solution

  • Why is localtime_r() allocating memory?

    The date/time functions in C runtime library need to know various information about timezones, like when does summer daylight saving time start and end etc. These are stored in the so called Timezone file.

    The first time some related date/time function is called, this file needs to be loaded, parsed and its data stored somewhere, so that further calls to these functions do not incur the same penalty again. The allocations you see are related to that.

    So is it a memory leak?

    This memory is allocated, but never freed during the execution of the program, and therefore can be considered a memory leak. But really it is not a bug, because it is a conscious decision by the library authors.

    As I wrote above, the data are loaded once and are (possibly) used ever since for the rest of the runtime of the program. They would be released only at the very end, before the process terminates. There is actually many structures with the same usage pattern in libc.

    So they thought that instead of going through all the allocations one by one, freeing the memory, they just leave it there, because in the next millisecond the process will end anyway and the kernel will reclaim all allocated memory back anyway (and much faster, while at it, the whole heap at once).

    So there is no way to get rid of this?

    Not really, there is! But not for a casual user... As Valgrind documentation states:

    The glibc authors realised that this behaviour causes leak checkers, such as Valgrind, to falsely report leaks in glibc, when a leak check is done at exit. In order to avoid this, they provided a routine called __libc_freeres specifically to make glibc release all memory it has allocated.

    As expected, the routines dealing with timezone file are indeed using this "freeres" mechanism, e.g. time/tzfile.c:

    libc_freeres_ptr (static time_t *transitions);
    

    Valgrind calls this routine before end, so that if you run it with the (default) tool memcheck, you don't see any of these "leaks". They should disappear even for your program, it's probably just massif which lists the allocations when they happen and not after everything has finished.

    You might have success calling __libc_freeres yourself, but it might also just crash, because libc still does some internal processing after the end of user's main() function and you might release its internal structures prematurely.