Skip to main content
 首页 » 编程设计

c++之为什么在boost进程间共享内存中分配的对象占用的内存比所需的更多

2024年02月27日18powertoolsteam

对于以下使用Boost进程间共享内存的程序,

#include <iostream> 
#include <boost/interprocess/mapped_region.hpp> 
#include <boost/interprocess/managed_shared_memory.hpp> 
#include <boost/interprocess/containers/list.hpp> 
#include <iostream> 
 
#define SHARED_MEMORY_NAME "SO12439099-MySharedMemory" 
#define DATAOUTPUT "OutputFromObject" 
#define INITIAL_MEM 650000 
#define STATE_MATRIX_SIZE 4 
 
using namespace std; 
namespace bip = boost::interprocess; 
 
 
class SharedObject 
{ 
public: 
    unsigned int tNumber; 
    bool pRcvdFlag; 
    bool sRcvdFlag; 
    unsigned long lTimeStamp;  
}; 
 
typedef bip::allocator<SharedObject, bip::managed_shared_memory::segment_manager> ShmemAllocator; 
typedef bip::list<SharedObject, ShmemAllocator> SharedMemData;  
 
 
int main() 
{ 
        bip::managed_shared_memory* seg; 
        SharedMemData *sharedMemOutputList; 
 
        bip::shared_memory_object::remove(DATAOUTPUT); 
        seg = new bip::managed_shared_memory(bip::create_only, DATAOUTPUT, INITIAL_MEM); 
        const ShmemAllocator alloc_inst(seg->get_segment_manager()); 
        sharedMemOutputList = seg->construct<SharedMemData>("TrackOutput")(alloc_inst); 
 
        std::size_t beforeAllocation = seg->get_free_memory(); 
        std::cout<<"\nBefore allocation = "<< beforeAllocation <<"\n"; 
        SharedObject temp; 
        sharedMemOutputList->push_back(temp); 
        std::size_t afterAllocation = seg->get_free_memory(); 
        std::cout<<"After allocation = "<< afterAllocation <<"\n";         
        std::cout<<"Difference = "<< beforeAllocation - afterAllocation <<"\n";         
        std::cout<<"Size of SharedObject = "<< sizeof(SharedObject) <<"\n";    
        std::cout<<"Size of SharedObject's temp instance = "<< sizeof(temp) <<"\n";            
        seg->destroy<SharedMemData>("TrackOutput"); 
        delete seg;             
}//main 

输出为:
Before allocation = 649680 
After allocation = 649632 
Difference = 48 
Size of SharedObject = 16 
Size of SharedObject's temp instance = 16 

如果 SharedObject及其实例的大小为16个字节,那么分配的差异如何为48?即使 padding是自动完成的,它仍然无法容纳3倍的大小(对于较大的结构,它达到了1.33倍的大小)。
因此,我无法可靠地分配和动态增长共享内存。如果 SharedObject包含一个动态增长的列表,则可能会进一步增加空间分配的不确定性。

如何安全处理这些情况?

ps:要运行该程序,必须链接 pthread库和 librt.so

更新:

这是我多次运行 tabulated值时得到的内存使用模式( memory increase列基本上是 memory used列的当前行减去 memory used column的前一行):

╔═════════════╦════════════════╦═════════════════╗
structure所使用的内存结构大小me增加的内存大小
╠═════════════╬════════════════╬═════════════════╣
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48
48 48 48 48 48 48 48 48 48 48 48 48 48 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4>
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 8 8 48 8 8 8 8 8 8 8> 8
48 48 48 48 48 48 48 48 48 48 48 48 48 48 16 16 48 48 16 16 16 16 16 16>
64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 16
64 64 64 64 64 64 64 64 64 64 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40>
80 80 80 80 80 80 80 80 80 80 80 80 80 80 48 48 48 48 48 48 48 48> 48
96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96 96>
160 160 160 160 160 160 128 128 128 128 128 128 128 128 128 128 128 160 160 160 160 128 160
288 288 288 288 288 288 288 288 256 256 288 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256
416 416 416 416 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384 384
544 544 544 544 544 544 544 544 544 544 544 544 544 544 544 544 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 512 128
800 800(800)800 800 800 800 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 768 256 768 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256
56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 256 256
╚═════════════牛皮════════════════牛皮═════════════════╝

重要:上表仅适用于共享内存 list。对于 vector,(使用的内存,结构大小)值=(48,1),(48,8),(48,16),(48,32),(80,64),(80,72), (112、96),(128、120),(176、168),(272、264),(544、528)。
因此,其他容器需要不同的内存计算公式。

请您参考如下方法:

请记住,任何通用分配机制都具有有效负载,以便存储有关如何取消分配该内存,如何将该缓冲区与相邻缓冲区合并等信息。这在您的系统malloc中发生(通常,每分配8-16个额外字节)加上额外的对齐方式)。共享内存中的内存分配器的开销为4-8字节(在32位系统中,在64位系统中为8-16)

然后,该库需要存储对象数,以便在调用“destroy_ptr(ptr)”时调用析构函数(您可以分配数组,因此需要知道应调用多少个析构函数)。而且您已经进行了命名分配,因此该库需要将该字符串存储在共享内存中,并需要一些元数据来找到它(指向该字符串的指针,也许这是“命名分配”,而不是“匿名”或“实例分配)。

因此,16个字节的数据+ 8个字节的内存分配器+ 8个字节的指针+元数据存储到名称+ 12个字节的字符串“TrackOutput”(包括空端)加上8个字节的对齐,您将获得48个字节。

每个分配的开销几乎是恒定的。因此,常数因子1.33仅适用于小额分配。如果分配单个字节,则会遇到更糟糕的因素,就像从堆分配单个char一样。

如果没有可用的内存来存储新对象,则库将引发异常,您可以捕获它并尝试创建新的托管共享内存。请注意,内存会因分配和重新分配而分散,因此即使共享内存中有可用字节,托管共享内存也无法满足您的请求,因为没有足够大的连续内存来满足请求。共享内存无法自动扩展,因此碎片问题比堆内存要大得多。

您无法动态增加共享内存,因为可能有其他进程连接到该共享内存,并且在尝试访问未映射的页面时会崩溃。唯一的选择是预先分配一个共享内存,这足以添加一个恒定的填充因子,或者分配一个新的托管共享内存,并通知所有其他读取器/写入器(也许使用原始共享内存中的预分配结构)新元素将要使用在新的托管共享内存中。