为什么 WebAssembly 中的 malloc 需要 4 倍的内存?
Why malloc in WebAssembly requires 4x the memory?
我用 C 编写了一个脚本,用于在无限循环中使用 malloc() 分配内存。
我的目标是通过打开多个选项卡并使浏览器崩溃来使用 WebAssembly 实现简单的拒绝服务。
我可以为每个选项卡最多分配大约 2 GB 以防止选项卡崩溃(x64 浏览器的内存限制)。
#include <stdlib.h>
#define MAX_MEM 2147483630 //2 GB
int main() {
long int mem_used=209715000;
while(1){
if(mem_used<MAX_MEM){
int *ptr = malloc(sizeof(int));
mem_used+=4;
}
}
return 0;
}
我希望它能正常工作,但选项卡却崩溃了。
根据我所做的测试,mem_used+=16 是防止选项卡崩溃的正确选择。
我不太了解 WebAssembly 内存管理,所以我的想法是它可能需要 4 倍的内存。对吗?
您的问题是 malloc 实现通常:
a) Include overhead; and
b) Round up to some unit
malloc (sizeof(int)) 在幕后使用了超过 sizeof(int) 个字节。
使用 emscripten,malloc 添加一些最小块大小,然后将地址对齐到至少 8 字节边界。因此对于小分配(甚至零字节),malloc 似乎比需要的 space 多得多。对于大分配,开销会相对较小。
查看 dlmalloc.c 中的评论。
下面的程序演示了 space malloc 占用了多少:
#include <iostream>
int main() {
char *previous, *current;
previous = (char*)malloc(0);
for(int i=0; i<32; ++i) {
current = (char*)malloc(i+1);
std::cout << "malloc(" << i << ") consumed " << (current-previous) << " bytes\n";
previous = current;
}
std::cout << "\n";
previous = (char*)malloc(1);
for(int i=0; i<12; ++i) {
current = (char*)malloc( 1<<(i+1) );
std::cout << "malloc(" << (1<<i) << ") consumed " << (current-previous) << " bytes\n";
previous = current;
}
return 0;
}
这会产生以下输出:
malloc(0) consumed 16 bytes
malloc(1) consumed 16 bytes
malloc(2) consumed 16 bytes
malloc(3) consumed 16 bytes
malloc(4) consumed 16 bytes
malloc(5) consumed 16 bytes
malloc(6) consumed 16 bytes
malloc(7) consumed 16 bytes
malloc(8) consumed 16 bytes
malloc(9) consumed 16 bytes
malloc(10) consumed 16 bytes
malloc(11) consumed 16 bytes
malloc(12) consumed 16 bytes
malloc(13) consumed 24 bytes
malloc(14) consumed 24 bytes
malloc(15) consumed 24 bytes
malloc(16) consumed 24 bytes
malloc(17) consumed 24 bytes
malloc(18) consumed 24 bytes
malloc(19) consumed 24 bytes
malloc(20) consumed 24 bytes
malloc(21) consumed 32 bytes
malloc(22) consumed 32 bytes
malloc(23) consumed 32 bytes
malloc(24) consumed 32 bytes
malloc(25) consumed 32 bytes
malloc(26) consumed 32 bytes
malloc(27) consumed 32 bytes
malloc(28) consumed 32 bytes
malloc(29) consumed 40 bytes
malloc(30) consumed 40 bytes
malloc(31) consumed 40 bytes
malloc(1) consumed 16 bytes
malloc(2) consumed 16 bytes
malloc(4) consumed 16 bytes
malloc(8) consumed 16 bytes
malloc(16) consumed 24 bytes
malloc(32) consumed 40 bytes
malloc(64) consumed 72 bytes
malloc(128) consumed 136 bytes
malloc(256) consumed 264 bytes
malloc(512) consumed 520 bytes
malloc(1024) consumed 1032 bytes
malloc(2048) consumed 2056 bytes
请参阅 this repo
中的完整源代码
在任何系统中,malloc()
使用的内存总是比您请求的多一些。 Emscripten uses dlmalloc
,一个流行的 malloc()
实现,默认.根据维基百科:
Memory on the heap is allocated as "chunks", an 8-byte aligned data structure which contains a header, and usable memory. Allocated memory contains an 8 or 16 byte overhead for the size of the chunk and usage flags. Unallocated chunks also store pointers to other free chunks in the usable space area, making the minimum chunk size 16 bytes (32-bit system) and 24 bytes (64-bit system).
这意味着即使是单字节分配的内存块malloc(1)
也至少使用 16 到 24 字节。这是因为内存对齐问题,每个分配的块都需要额外的字节来存储块的元数据。您可以轻松 google malloc()
的工作原理来理解为什么会有这样的开销。
因此,为了满足您的目的,测试应该在每次迭代时分配更大的内存块,以尽量减少此类开销。我个人会推荐 4kb 或 1MB 而不是 sizeof(int)。
我用 C 编写了一个脚本,用于在无限循环中使用 malloc() 分配内存。 我的目标是通过打开多个选项卡并使浏览器崩溃来使用 WebAssembly 实现简单的拒绝服务。 我可以为每个选项卡最多分配大约 2 GB 以防止选项卡崩溃(x64 浏览器的内存限制)。
#include <stdlib.h>
#define MAX_MEM 2147483630 //2 GB
int main() {
long int mem_used=209715000;
while(1){
if(mem_used<MAX_MEM){
int *ptr = malloc(sizeof(int));
mem_used+=4;
}
}
return 0;
}
我希望它能正常工作,但选项卡却崩溃了。 根据我所做的测试,mem_used+=16 是防止选项卡崩溃的正确选择。 我不太了解 WebAssembly 内存管理,所以我的想法是它可能需要 4 倍的内存。对吗?
您的问题是 malloc 实现通常:
a) Include overhead; and
b) Round up to some unit
malloc (sizeof(int)) 在幕后使用了超过 sizeof(int) 个字节。
使用 emscripten,malloc 添加一些最小块大小,然后将地址对齐到至少 8 字节边界。因此对于小分配(甚至零字节),malloc 似乎比需要的 space 多得多。对于大分配,开销会相对较小。
查看 dlmalloc.c 中的评论。
下面的程序演示了 space malloc 占用了多少:
#include <iostream>
int main() {
char *previous, *current;
previous = (char*)malloc(0);
for(int i=0; i<32; ++i) {
current = (char*)malloc(i+1);
std::cout << "malloc(" << i << ") consumed " << (current-previous) << " bytes\n";
previous = current;
}
std::cout << "\n";
previous = (char*)malloc(1);
for(int i=0; i<12; ++i) {
current = (char*)malloc( 1<<(i+1) );
std::cout << "malloc(" << (1<<i) << ") consumed " << (current-previous) << " bytes\n";
previous = current;
}
return 0;
}
这会产生以下输出:
malloc(0) consumed 16 bytes
malloc(1) consumed 16 bytes
malloc(2) consumed 16 bytes
malloc(3) consumed 16 bytes
malloc(4) consumed 16 bytes
malloc(5) consumed 16 bytes
malloc(6) consumed 16 bytes
malloc(7) consumed 16 bytes
malloc(8) consumed 16 bytes
malloc(9) consumed 16 bytes
malloc(10) consumed 16 bytes
malloc(11) consumed 16 bytes
malloc(12) consumed 16 bytes
malloc(13) consumed 24 bytes
malloc(14) consumed 24 bytes
malloc(15) consumed 24 bytes
malloc(16) consumed 24 bytes
malloc(17) consumed 24 bytes
malloc(18) consumed 24 bytes
malloc(19) consumed 24 bytes
malloc(20) consumed 24 bytes
malloc(21) consumed 32 bytes
malloc(22) consumed 32 bytes
malloc(23) consumed 32 bytes
malloc(24) consumed 32 bytes
malloc(25) consumed 32 bytes
malloc(26) consumed 32 bytes
malloc(27) consumed 32 bytes
malloc(28) consumed 32 bytes
malloc(29) consumed 40 bytes
malloc(30) consumed 40 bytes
malloc(31) consumed 40 bytes
malloc(1) consumed 16 bytes
malloc(2) consumed 16 bytes
malloc(4) consumed 16 bytes
malloc(8) consumed 16 bytes
malloc(16) consumed 24 bytes
malloc(32) consumed 40 bytes
malloc(64) consumed 72 bytes
malloc(128) consumed 136 bytes
malloc(256) consumed 264 bytes
malloc(512) consumed 520 bytes
malloc(1024) consumed 1032 bytes
malloc(2048) consumed 2056 bytes
请参阅 this repo
中的完整源代码在任何系统中,malloc()
使用的内存总是比您请求的多一些。 Emscripten uses dlmalloc
,一个流行的 malloc()
实现,默认.根据维基百科:
Memory on the heap is allocated as "chunks", an 8-byte aligned data structure which contains a header, and usable memory. Allocated memory contains an 8 or 16 byte overhead for the size of the chunk and usage flags. Unallocated chunks also store pointers to other free chunks in the usable space area, making the minimum chunk size 16 bytes (32-bit system) and 24 bytes (64-bit system).
这意味着即使是单字节分配的内存块malloc(1)
也至少使用 16 到 24 字节。这是因为内存对齐问题,每个分配的块都需要额外的字节来存储块的元数据。您可以轻松 google malloc()
的工作原理来理解为什么会有这样的开销。
因此,为了满足您的目的,测试应该在每次迭代时分配更大的内存块,以尽量减少此类开销。我个人会推荐 4kb 或 1MB 而不是 sizeof(int)。