- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
在过去一周左右的时间里,我一直在调查内存使用量随时间累积的应用程序中的问题。我将其缩小到复制 a
std::vector< std::vector< std::vector< std::map< uint, map< uint, std::bitset< N> > > > > >
在工作线程中(我意识到这是一种组织内存的荒谬方式)。定期地,工作线程被销毁、重新创建,并且该线程在启动时复制该内存结构。复制的原始数据通过引用从主线程传递到工作线程。
使用 malloc_stat 和 malloc_info,我可以看到当工作线程被销毁时,它正在使用的 arena/heap 在它的 fastbins 空闲列表中保留了用于该结构的内存。这是有道理的,因为有许多小于 64 字节的单独分配。
问题是,当工作线程被重新创建时,它会创建一个新的 arena/heap 而不是重复使用之前的 arena/heap,这样来自之前 arenas/heaps 的 fastbins 永远不会被重复使用。最终,系统在重用之前的堆/竞技场以重用它们持有的 fastbins 之前耗尽内存。
有点偶然,我发现在我的主线程中调用 malloc_trim(0),在加入工作线程后,导致线程 arenas/heaps 中的 fastbins 被释放。据我所知,这种行为没有记录。有人有解释吗?
这是我用来查看此行为的一些测试代码:
// includes
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <mcheck.h>
#include <malloc.h>
#include <map>
#include <bitset>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
// Number of bits per bitset.
const int sizeOfBitsets = 40;
// Executes a system command. Used to get output of "free -m".
std::string ExecuteSystemCommand(const char* cmd) {
char buffer[128];
std::string result = "";
FILE* pipe = popen(cmd, "r");
if (!pipe) throw std::runtime_error("popen() failed!");
try {
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
// Prints output of "free -m" and output of malloc_stat().
void PrintMemoryStats()
{
try
{
char *buf;
size_t size;
FILE *fp;
std::string myCommand("free -m");
std::string result = ExecuteSystemCommand(myCommand.c_str());
printf("Free memory is \n%s\n", result.c_str());
malloc_stats();
fp = open_memstream(&buf, &size);
malloc_info(0, fp);
fclose(fp);
printf("# Memory Allocation Stats\n%s\n#> ", buf);
free(buf);
}
catch(...)
{
printf("Unable to print memory stats.\n");
throw;
}
}
void MakeCopies(std::vector<std::vector<std::map<uint, std::map<uint, std::bitset<sizeOfBitsets> > > > >& data)
{
try
{
// Create copies.
std::vector<std::vector<std::map<uint, std::map<uint, std::bitset<sizeOfBitsets> > > > > dataCopyA(data);
std::vector<std::vector<std::map<uint, std::map<uint, std::bitset<sizeOfBitsets> > > > > dataCopyB(data);
std::vector<std::vector<std::map<uint, std::map<uint, std::bitset<sizeOfBitsets> > > > > dataCopyC(data);
// Print memory info.
printf("Memory after creating data copies:\n");
PrintMemoryStats();
}
catch(...)
{
printf("Unable to make copies.");
throw;
}
}
int main(int argc, char** argv)
{
try
{
// When uncommented, disables the use of fastbins.
// mallopt(M_MXFAST, 0);
// Print memory info.
printf("Memory to start is:\n");
PrintMemoryStats();
// Sizes of original data.
int sizeOfDataA = 2048;
int sizeOfDataB = 4;
int sizeOfDataC = 128;
int sizeOfDataD = 20;
std::vector<std::vector<std::map<uint, std::map<uint, std::bitset<sizeOfBitsets> > > > > testData;
// Populate data.
testData.resize(sizeOfDataA);
for(int a = 0; a < sizeOfDataA; ++a)
{
testData.at(a).resize(sizeOfDataB);
for(int b = 0; b < sizeOfDataB; ++b)
{
for(int c = 0; c < sizeOfDataC; ++c)
{
std::map<uint, std::bitset<sizeOfBitsets> > dataMap;
testData.at(a).at(b).insert(std::pair<uint, std::map<uint, std::bitset<sizeOfBitsets> > >(c, dataMap));
for(int d = 0; d < sizeOfDataD; ++d)
{
std::bitset<sizeOfBitsets> testBitset;
testData.at(a).at(b).at(c).insert(std::pair<uint, std::bitset<sizeOfBitsets> >(d, testBitset));
}
}
}
}
// Print memory info.
printf("Memory to after creating original data is:\n");
PrintMemoryStats();
// Start thread to make copies and wait to join.
{
boost::shared_ptr<boost::thread> makeCopiesThread = boost::shared_ptr<boost::thread>(new boost::thread(&MakeCopies, boost::ref(testData)));
makeCopiesThread->join();
}
// Print memory info.
printf("Memory to after joining thread is:\n");
PrintMemoryStats();
malloc_trim(0);
// Print memory info.
printf("Memory to after malloc_trim(0) is:\n");
PrintMemoryStats();
return 0;
}
catch(...)
{
// Log warning.
printf("Unable to run application.");
// Return failure.
return 1;
}
// Return success.
return 0;
}
malloc trim 调用前后的有趣输出是(寻找“看这里!”):
#> Memory to after joining thread is:
Free memory is
total used free shared buff/cache available
Mem: 257676 7361 246396 25 3918 249757
Swap: 1023 0 1023
Arena 0:
system bytes = 1443450880
in use bytes = 1443316976
Arena 1:
system bytes = 35000320
in use bytes = 6608
Total (incl. mmap):
system bytes = 1478451200
in use bytes = 1443323584
max mmap regions = 0
max mmap bytes = 0
# Memory Allocation Stats
<malloc version="1">
<heap nr="0">
<sizes>
<size from="241" to="241" total="241" count="1"/>
<size from="529" to="529" total="529" count="1"/>
</sizes>
<total type="fast" count="0" size="0"/>
<total type="rest" count="2" size="770"/>
<system type="current" size="1443450880"/>
<system type="max" size="1443459072"/>
<aspace type="total" size="1443450880"/>
<aspace type="mprotect" size="1443450880"/>
</heap>
<heap nr="1">
<sizes>
<size from="33" to="48" total="48" count="1"/>
<size from="49" to="64" total="4026531712" count="62914558"/> <-- LOOK HERE!
<size from="65" to="80" total="160" count="2"/>
<size from="81" to="96" total="301989888" count="3145728"/> <-- LOOK HERE!
<size from="33" to="33" total="231" count="7"/>
<size from="49" to="49" total="1274" count="26"/>
<unsorted from="0" to="49377" total="1431600" count="6144"/>
</sizes>
<total type="fast" count="66060289" size="4328521808"/>
<total type="rest" count="6177" size="1433105"/>
<system type="current" size="4329967616"/>
<system type="max" size="4329967616"/>
<aspace type="total" size="35000320"/>
<aspace type="mprotect" size="35000320"/>
</heap>
<total type="fast" count="66060289" size="4328521808"/>
<total type="rest" count="6179" size="1433875"/>
<total type="mmap" count="0" size="0"/>
<system type="current" size="5773418496"/>
<system type="max" size="5773426688"/>
<aspace type="total" size="1478451200"/>
<aspace type="mprotect" size="1478451200"/>
</malloc>
#> Memory to after malloc_trim(0) is:
Free memory is
total used free shared buff/cache available
Mem: 257676 3269 250488 25 3918 253850
Swap: 1023 0 1023
Arena 0:
system bytes = 1443319808
in use bytes = 1443316976
Arena 1:
system bytes = 35000320
in use bytes = 6608
Total (incl. mmap):
system bytes = 1478320128
in use bytes = 1443323584
max mmap regions = 0
max mmap bytes = 0
# Memory Allocation Stats
<malloc version="1">
<heap nr="0">
<sizes>
<size from="209" to="209" total="209" count="1"/>
<size from="529" to="529" total="529" count="1"/>
<unsorted from="0" to="49377" total="1431600" count="6144"/>
</sizes>
<total type="fast" count="0" size="0"/>
<total type="rest" count="6146" size="1432338"/>
<system type="current" size="1443459072"/>
<system type="max" size="1443459072"/>
<aspace type="total" size="1443459072"/>
<aspace type="mprotect" size="1443459072"/>
</heap>
<heap nr="1"> <---------------------------------------- LOOK HERE!
<sizes> <-- HERE!
<unsorted from="0" to="67108801" total="4296392384" count="6208"/>
</sizes>
<total type="fast" count="0" size="0"/>
<total type="rest" count="6208" size="4296392384"/>
<system type="current" size="4329967616"/>
<system type="max" size="4329967616"/>
<aspace type="total" size="35000320"/>
<aspace type="mprotect" size="35000320"/>
</heap>
<total type="fast" count="0" size="0"/>
<total type="rest" count="12354" size="4297824722"/>
<total type="mmap" count="0" size="0"/>
<system type="current" size="5773426688"/>
<system type="max" size="5773426688"/>
<aspace type="total" size="1478459392"/>
<aspace type="mprotect" size="1478459392"/>
</malloc>
#>
关于 malloc_info 输出的文档几乎没有,所以我不确定我指出的那些输出是否真的是快速 bin。为了验证它们确实是 fastbins,我取消了代码行的注释
mallopt(M_MXFAST, 0);
在加入线程后,在调用 malloc_trim(0) 之前禁用 fastbins 和堆 1 的内存使用,看起来就像在调用 malloc_trim(0) 之后启用 fastbins 一样。最重要的是,禁用 fastbins 的使用会在线程加入后立即将内存返回给系统。在启用 fastbins 的情况下加入线程后,调用 malloc_trim(0) 也会将内存返回给系统。
malloc_trim(0) 的文档指出它只能从主竞技场堆的顶部释放内存,那么这里发生了什么?我在 CentOS 7 上运行 glibc 版本 2.17。
最佳答案
malloc_trim(0) states that it can only free memory from the top of the main arena heap, so what is going on here?
它可以被称为“过时”或“不正确”的文档。 Glibc 没有 documentation of malloc_trim
function ; Linux 使用手册页项目中的手册页。 malloc_trim
http://man7.org/linux/man-pages/man3/malloc_trim.3.html was written in 2012 by maintainer of man-pages 的手册页是新的。可能他使用了 glibc malloc/malloc.c 源代码 http://code.metager.de/source/xref/gnu/glibc/malloc/malloc.c#675 中的一些注释
676 malloc_trim(size_t pad);
677
678 If possible, gives memory back to the system (via negative
679 arguments to sbrk) if there is unused memory at the `high' end of
680 the malloc pool. You can call this after freeing large blocks of
681 memory to potentially reduce the system-level memory requirements
682 of a program. However, it cannot guarantee to reduce memory. Under
683 some allocation patterns, some large free blocks of memory will be
684 locked between two used chunks, so they cannot be given back to
685 the system.
686
687 The `pad' argument to malloc_trim represents the amount of free
688 trailing space to leave untrimmed. If this argument is zero,
689 only the minimum amount of memory to maintain internal data
690 structures will be left (one page or less). Non-zero arguments
691 can be supplied to maintain enough trailing space to service
692 future expected allocations without having to re-obtain memory
693 from the system.
694
695 Malloc_trim returns 1 if it actually released any memory, else 0.
696 On systems that do not support "negative sbrks", it will always
697 return 0.
glibc 中的实际实现是 __malloc_trim
并且它具有迭代 arenas 的代码:
http://code.metager.de/source/xref/gnu/glibc/malloc/malloc.c#4552
4552 int
4553 __malloc_trim (size_t s)
4560 mstate ar_ptr = &main_arena;
4561 do
4562 {
4563 (void) mutex_lock (&ar_ptr->mutex);
4564 result |= mtrim (ar_ptr, s);
4565 (void) mutex_unlock (&ar_ptr->mutex);
4566
4567 ar_ptr = ar_ptr->next;
4568 }
4569 while (ar_ptr != &main_arena);
每个竞技场都使用 mtrim()
(mTRIm()
) 函数进行修剪,该函数调用 malloc_consolidate()
将所有空闲段从fastbins(它们不会在空闲时合并,因为它们很快)到普通空闲 block (与相邻 block 合并)
4498 /* Ensure initialization/consolidation */
4499 malloc_consolidate (av);
4111 malloc_consolidate is a specialized version of free() that tears
4112 down chunks held in fastbins.
1581 Fastbins
1591 Chunks in fastbins keep their inuse bit set, so they cannot
1592 be consolidated with other free chunks. malloc_consolidate
1593 releases all chunks in fastbins and consolidates them with
1594 other free chunks.
The problem is, when the worker thread is recreated, it creates a new arena/heap instead of reusing the previous one, such that the fastbins from previous arenas/heaps are never reused.
这很奇怪。根据设计,glibc malloc 中的最大竞技场数受 cpu_core_count * 8(对于 64 位平台)的限制; cpu_core_count * 2(对于 32 位平台)或通过环境变量 MALLOC_ARENA_MAX
/mallopt
参数 M_ARENA_MAX
。
您可以限制应用程序的竞技场数量;定期调用 malloc_trim()
或以“大”大小调用 malloc()
(它将调用 malloc_consolidate
),然后 free( )
在销毁之前从您的线程获取它:
3319 _int_malloc (mstate av, size_t bytes)
3368 if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
// fastbin allocation path
3405 if (in_smallbin_range (nb))
// smallbin path; malloc_consolidate may be called
3437 If this is a large request, consolidate fastbins before continuing.
3438 While it might look excessive to kill all fastbins before
3439 even seeing if there is space available, this avoids
3440 fragmentation problems normally associated with fastbins.
3441 Also, in practice, programs tend to have runs of either small or
3442 large requests, but less often mixtures, so consolidation is not
3443 invoked all that often in most programs. And the programs that
3444 it is called frequently in otherwise tend to fragment.
3445 */
3446
3447 else
3448 {
3449 idx = largebin_index (nb);
3450 if (have_fastchunks (av))
3451 malloc_consolidate (av);
3452 }
PS: malloc_trim
https://github.com/mkerrisk/man-pages/commit/a15b0e60b297e29c825b7417582a33e6ca26bf65 的手册页中有注释:
+.SH NOTES
+This function only releases memory in the main arena.
+.\" malloc/malloc.c::mTRIm():
+.\" return result | (av == &main_arena ? sYSTRIm (pad, av) : 0);
是的,有对 main_arena 的检查,但它在 malloc_trim
实现 mTRIm()
的最后,它只是为了调用 sbrk()
负偏移量。 Since 2007 (glibc 2.9 and newer) there is another method 将内存返回给操作系统:madvise(MADV_DONTNEED)
用于所有领域(glibc 补丁的作者或手册页的作者未记录)。每个领域都需要整合。还有用于修剪(munmapping)顶部 block 的 mmap 堆的代码(heap_trim
/shrink_heap
从慢速路径 free() 调用),但它不是从 malloc_trim
。
关于c++ - malloc_trim(0) 释放 Thread Arenas 的 Fastbins?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40513716/
完成此任务的最佳方法是什么:主线程(线程 A)创建另外两个线程(线程 B 和线程 C)。线程 B 和 C 执行繁重的磁盘 I/O,最终需要将它们创建的资源传递给线程 A,然后调用外部 DLL 文件中的
我是一名对 Julia 语言感兴趣的新手程序员。文档( https://docs.julialang.org/en/v1/base/multi-threading/ )说 Threads.@threa
python中的thread.start_new_thread和threading.Thread.start有什么区别? 我注意到,当调用 start_new_thread 时,新线程会在调用线程终止
我正在学习安卓蓝牙编程。我从 Google 的 Android 开发者网站上复制了大部分代码以供学习。这个想法是监听服务器上的连接是在一个新线程中完成的,而不会阻塞 UI 线程。当收到连接请求时,连接
执行对象方法的线程是否依赖于创建它的线程上的线程? 假设您的 java 应用程序中有两个线程 Thread1 和 Thread2,以及两个类 ClassA 和 ClassB。 您在 Thread1 上
我正在用这段代码测试 C++11 线程,但是在创建线程时,我遇到了错误没有匹配函数调用 'std::thread::thread()'. 这就像我给 std::thread ctr 的函数有什么问题,
我有如下类 eventEngine 和网关: class eventEngine { public: eventEngine(); std::thread threa; std
我需要运行与列表 dirlist 中的元素一样多的 Observer 类线程。当我运行它 python 控制台时,它可以正常工作。 class Observer(Thread): def ru
我在一本 Java 书中读到了下面的代码。我知道主类默认继承 Thread 类,所以 currentThread();而不是 Thread.currentThread();也会做这项工作。 但我不明白
我在我的系统中使用第 3 方 API,该 API 启动一个永久运行的用户线程。一旦我的程序结束,JVM 由于该线程而继续运行,因此我尝试获取此线程引用并通过更改它 thread.setDaemon(t
我所知道的 Python 中的每个对象都可以通过调用来处理其基类初始化: super(BaseClass, self).__init__() threading.Thread 的子类似乎不是这种情况,
在我最近从事的 Xamarin 项目中,我可以看到开发人员使用了 Java.Lang.Thread 以及 System.Threading.Thread(用于非常相似的操作 - 例如在后台加载数据)。
我在 Julia 中运行双循环。代码非常简单。 w = rand(1000,1000) function regular_demo(w::Array{Float64, 2}) n = size
我在 Windows 上使用 Python 3。我正在使用 threading.Thread动态运行一个函数,我可以带参数或不带参数调用它。我正在设置一个列表,其中的第一项是定义路径的字符串。其他参数
我遇到了一些使用线程模块(使用 Python 2.6)管理线程的示例。 我想了解的是这个例子是如何调用“运行”方法的,在哪里调用的。我在任何地方都看不到它。 ThreadUrl 类在 main() 函
假设我从 threading.Thread 派生: from threading import Thread class Worker(Thread): def start(self):
关闭。这个问题需要debugging details .它目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and th
使用 WinDbg 和 SOS,我有以下内容: 0:011> !threads ThreadCount: 7 UnstartedThread: 0 BackgroundThread: 4 Pendin
App Engine 给出错误: com.google.apphosting.api.ApiProxy$CallNotFoundException: Can't make API call urlfe
我正在尝试将 Swing JEditorPane 嵌入到 JavaFX 项目中,如下代码所示。 Platform.runLater(() -> { SyntaxTester ob = new
我是一名优秀的程序员,十分优秀!