我正在处理大量Rust 的数据,在判断数据 struct 的内存使用情况时,我偶然发现了一些令人惊讶的结果.

首先,我试着手动填充一个向量,只需将其按零即可:

let mut arr = vec![];

for _ in 0..(4_294_967_294 as u32) {
    arr.push(0);
}

过了一会儿,我很期待地说,我的计算机耗尽了可用内存,进程被操作系统终止.

但是,如果我使用宏初始化来初始化向量,行为会发生变化:

let mut arr = vec![0; 2_147_483_647_000];

for i in 1..1_000_000_000 {
    arr[i-1] = rng.next_u64();

    let sample = rng.next_u32();
    let res = arr[sample as usize];
    if i % 10000000 == 0 {
        print!("After {} ", i);
        print!("Btw, arr[{}] == {} ", sample, res);
        print_allocated_memory();
    }
}

尽管我用一个实际的u64值填充了10亿个条目,并从数组中读出了随机值(主要是零,我只是试图排除编译器对整个数组的优化),但我的计算机内存并没有溢出.

jemalloc个内存使用率如下(请注意,我的电脑只安装了16 GB的RAM):

allocated 16777216.05 MiB resident 16777223.02 MiB

... 而我的操作系统在代码末尾报告的最大值约为8000米(以htop为单位).

奇怪的是,如果我使用0以外的任何其他默认值(无论是1还是100),宏在完成向量创建之前就会耗尽内存,因此它肯定与init值为0有关.

我想知道宏做了什么来保持结果数据 struct 的内存效率?数组中的元素不是真的创建的吗?如果没有,那么我如何从向量中读出随机指数呢?

我已经判断了documentation,尽管它只说它依赖于Clone类型的默认元素,这对基本类型来说并没有任何意义.

推荐答案

为向量分配内存时,可以使用一些内置的分配功能.当向量的类型为数字且给定的初始值为零时,使用__rust_alloc_zeroed.

在Unix兼容系统上,此分配器函数的默认实现可以使用calloc()posix_memalign().

calloc()保证分配归零;posix_memalign()没有.如果使用后者,Rust分配器会将内存本身归零.

考虑到你观察到的行为,唯一合理的解释是使用calloc().由于库使用之前释放的内存(分配肯定太大)无法满足请求,因此该请求被传递到内核,内核在进程的页表中为请求的分配创建条目.

然而,操作系统实际上不必为分配中的每个页面分配一个物理内存区域.它可以推迟到以后,一种叫做overcommitment的技术.

如果分配区域中的地址尚未得到物理内存的支持,则读取或写入该地址将触发页面错误.当这个错误发生时,内核通过给被访问的页面分配一个内存区域来解决它.

所有这一切的最终结果是,如果你创建一个初始值为零的数值类型的向量,那么最初分配实际上使用的系统内存非常少.几乎所有的分配都在还没有备份系统内存的页面中,类似于稀疏文件中的漏洞.当您写入向量时,系统将开始为分配分配分配物理内存,您的已用内存(和/或已用交换)将开始增加.

Rust相关问答推荐

有条件默认实现

Rust kill std::processs::child

Arrow RecordBatch as Polars DataFrame

如何为utoipa中的可选查询参数生成OpenAPI模式?

编译项目期间使用Cargo生成时出现rustc错误

带参考文献的 rust 元组解构

Rust&;Tokio:如何处理更多的信号,而不仅仅是SIGINT,即SIGQUE?

为什么Deref类特征不构成?

如何在 Rust 中编写一个通用方法,它可以接受任何可以转换为另一个值的值?

如何从borrow 的异步代码运行阻塞代码?

方法可以被误认为是标准特性方法

bcrypt 有长度限制吗?

为什么可以在迭代器引用上调用 into_iter?

为什么在 rust 中删除 vec 之前应该删除元素

将一片字节复制到一个大小不匹配的数组中

我如何将 google_gmail1::Gmail> 传递给线程生成?

用逗号分隔字符串,但在标记中使用逗号

相互调用的递归异步函数:检测到循环

在传输不可复制的值时实现就地枚举修改

为什么我返回的 impl Trait 的生命周期限制在其输入的生命周期内?