下面的代码在单线程中使用约 150MB,但在 100 个线程中使用几 GB:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let f = Arc::new(Mutex::new(Foo::new("hello")));
let mut threads = vec![];
for i in 0..100 {
let f = f.clone();
let t = thread::spawn(move || loop {
let mut locked = f.lock().unwrap();
*locked = Foo::new("hello");
drop(locked);
println!("{} reloaded", i);
thread::yield_now();
});
threads.push(t);
}
threads.into_iter().for_each(|h| h.join().unwrap());
}
pub struct Foo {
_data: Vec<String>,
}
impl Foo {
fn new(s: &str) -> Foo {
Foo {
_data: vec![s.to_owned(); 1024 * 1024],
}
}
}
持有 LockGuard
时,线程应该具有独占访问权限。因此,新的 Foo
应该被分配,旧的值应该在那个时候被丢弃。因此,从多个线程调用时使用这么多内存对我来说没有任何意义。
谁能解释一下为什么这段代码占用了这么多内存?
即使有 1000 个线程,Java 中的类似代码也能保持约 200mb 的内存。
import java.util.ArrayList;
import java.util.List;
public class Foo {
private List<String> data;
public static void main(String[] args) {
Foo f = new Foo();
for (int i = 0; i < 1000; i++) {
int n = i;
new Thread(() -> {
while (true) {
f.update();
System.gc();
System.out.println(n + " updated");
}
}).start();
}
}
public synchronized void update() {
data = new ArrayList<>(1024 * 1024);
for (int i = 0; i < 1024 * 1024; i++) {
data.add(new String("hello"));
}
}
}
最佳答案
所以问题出在大量的 glibc 的 malloc arenas 上,
每个竞技场都有预分配内存的缓存。检查它的简单方法是使用 MALLOC_ARENA_MAX=2
运行二进制文件,但最终解决方案取决于使用模式,有很多变量可以调整 glibc 的分配器:http://man7.org/linux/man-pages/man3/mallopt.3.html .
Java虚拟机其实也受malloc的allocator的影响。根据我的经验,有时配置 arena 的数量是合适的,以防止 docker 中的 jvm 使用大量内存。
关于rust - 为什么多个线程在持有 Mutex 时使用太多内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58110081/