我使用 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 地 block 的最后一张快照的 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)
在我的程序退出之前有没有办法释放它? (根据我的理解,当程序退出时它会被清除)
最佳答案
为什么 localtime_r()
分配内存?
C 运行时库中的日期/时间函数需要了解有关时区的各种信息,例如夏季夏令时何时开始和结束等。这些存储在所谓的Timezone file 中。 .
第一次调用一些相关的日期/时间函数时,需要加载、解析该文件并将其数据存储在某处,以便对这些函数的进一步调用不会再次招致相同的惩罚。您看到的分配与此相关。
那么是内存泄漏吗?
此内存已分配,但在程序执行期间从未释放,因此可以认为是内存泄漏。但实际上这不是错误,因为这是库作者的有意识决定。
正如我在上面所写的,数据被加载一次并且(可能)在程序的其余运行时间中一直被使用。它们只会在最后,即进程终止之前被释放。在 libc 中实际上有许多具有相同使用模式的结构。
所以他们认为与其一一处理所有分配,释放内存,不如将其留在那里,因为在接下来的毫秒内,进程无论如何都会结束,并且内核无论如何都会回收所有分配的内存(并且快得多,同时,整个堆一次)。
所以没有办法摆脱这个?
不是真的,有!但不适合临时用户...作为Valgrind documentation状态:
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.
正如预期的那样,处理时区文件的例程确实使用了这种“freeres”机制,例如time/tzfile.c
:
libc_freeres_ptr (static time_t *transitions);
Valgrind 在结束之前调用此例程,因此如果您使用(默认)工具 memcheck 运行它,您不会看到任何这些“泄漏”。即使对于您的程序,它们也应该消失,它可能只是 massif,它会在分配发生时列出分配,而不是在一切完成之后。
您自己调用 __libc_freeres
可能会成功,但它也可能会崩溃,因为 libc 仍在用户的 main()
函数结束后进行一些内部处理,而您可能会过早释放其内部结构。
关于c++ - localtime_r 在程序退出前消耗了一些内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23541583/