localtime_r在程序退出之前消耗了一些内存

时间:2014-05-08 12:19:02

标签: c++ memory-leaks valgrind localtime massif

我使用valgrind的massif来跟踪程序退出前的最后阶段的内存使用情况 并找到了

  • js :: DateTimeInfo :: updateTimeZoneAdjustment()(DateTime.cpp:19)

调用localtime_r并占用一些内存。

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)
来自valgrind's massif

的最后一个快照的ms_print
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)

在我的程序退出之前有没有解决这个问题? (根据我的理解,当程序退出时将被清除)

1 个答案:

答案 0 :(得分:1)

为什么localtime_r()分配内存?

C运行时库中的日期/时间函数需要知道有关时区的各种信息,例如夏季夏令时开始和结束时间等。这些都存储在所谓的Timezone file中。

第一次调用某个相关的日期/时间函数时,需要加载,解析此文件并将其数据存储在某处,以便进一步调用这些函数不会再次受到同样的惩罚。您看到的分配与此相关。

那么是内存泄漏吗?

此内存已分配,但在程序执行期间从未释放,因此可被视为内存泄漏。但实际上它不是一个错误,因为它是图书馆作者的有意识的决定。

正如我上面所写,数据被加载一次,并且(可能)用于程序的其余运行时间。在流程终止之前,它们只会在最后发布。实际上在libc中有许多具有相同使用模式的结构。

所以他们认为,不是逐个完成所有的分配,释放内存,而是将它留在那里,因为在接下来的毫秒内,进程无论如何都将结束,内核将回收所有分配的内存(和更快,在它的同时,整个堆一次)。

所以没有办法摆脱这个?

不是真的,有!但不适合随意的用户......正如Valgrind documentation所述:

  

glibc的作者意识到,当退出时进行泄漏检查时,这种行为会导致泄漏检查程序(例如Valgrind)错误地报告glibc中的泄漏。为了避免这种情况,他们提供了一个名为__libc_freeres的例程,专门用于让glibc释放它已分配的所有内存。

正如预期的那样,处理时区文件的例程确实正在使用这个" freeres"机制,例如time/tzfile.c

libc_freeres_ptr (static time_t *transitions);

Valgrind在结束之前调用此例程,因此如果使用(默认)工具memcheck运行它,您将看不到任何这些"泄漏"。即使你的程序,它们也应该消失,它可能只是 massif ,它列出了发生时的分配,而不是在一切都完成之后。

您可能自己成功调用__libc_freeres,但它也可能崩溃,因为libc在用户的main()函数结束后仍会进行一些内部处理,您可能会释放其内部结构过早。