< 返回版块

zhongsigang 发表于 2021-10-01 22:31

Tags:capacity,alloc

String在增加内容时,内部要重新分配地址吧?但似乎没有。代码如下:

#![feature(vec_into_raw_parts)]

fn main() {
    let s = String::from("hello");
    unsafe {
        let (ptr, len, cap) = s.into_raw_parts();
        println!("{:p},{},{}", ptr, len, cap);

        let x = Box::new(3); //干扰一下地址分配,免得后面的内存申请重用旧地址
        println!("{}", x);

        let mut s = String::from_raw_parts(ptr, len, cap);

        let y = Box::new(4); //再次干扰
        println!("{}", y);

        s.push_str(" world"); //应该新分配内存了吧

        let (ptr, len, cap) = s.into_raw_parts();
        println!("{:p},{},{}", ptr, len, cap); //但没有,为什么?
    }
}

是在playground上用nightly测试的。


Ext Link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b3b941499de85c6a77161a7f10553381

评论区

写评论
Bai-Jinlin 2021-10-07 16:29

肯定一些情况会重分配呀

fn main() {
    let mut s = "1".to_string();
    println!("{:p}", s.as_ptr());

    let _ = Box::new(1);

    s.push_str(&('a'..'z').collect::<String>());
    println!("{:p}", s.as_ptr());
}

这么写大概率地址不一样。

Grobycn 2021-10-07 11:43
#![feature(vec_into_raw_parts)]

fn main() {
    let s = String::from("hello");
    unsafe {
        let (ptr, len, cap) = s.into_raw_parts();
        println!("{:p},{},{}", ptr, len, cap);

        let x = Box::new(3); //干扰一下地址分配,免得后面的内存申请重用旧地址
        println!("{}", x);
        
        let x_addr = &*x as *const _ as usize;
        let max_cap = x_addr - (ptr as usize);
        
        println!("{}", max_cap); // 不超过 x 的情况下,最大 cap

        let mut s = String::from_raw_parts(ptr, len, cap);
        
        while s.capacity() < max_cap {     // 一直增长到超过 x 的地址
            let (ptr, len, cap) = s.into_raw_parts();
            println!("{:p},{},{}", ptr, len, cap); // 地址没有变化,cap 很小的情况下,增涨策略是翻倍
            s = String::from_raw_parts(ptr, len, cap);
            s.push_str(" ");
        }
        let (ptr, len, cap) = s.into_raw_parts();
        println!("{:p},{},{}", ptr, len, cap); // 地址变了
    }
}
作者 zhongsigang 2021-10-05 01:37

应该是重新分配空间时有优化,后面有连续空间可利用时就直接扩容。若干扰数据太多,原空间旁边的未分配空间不够用时才全部搬新家。

我用bumpalo测试,地址是改变了的。

guotie 2021-10-04 18:31

也有可能是追加的数据太少了。

初次分配可能会分配一部分内存,然后,追加的长度还够用的情况下不需要重新分配。

我刚才测试,追加很长的字符串,地址发生了变化。

作者 zhongsigang 2021-10-02 10:18

我学习一下grow,感谢,祝国庆快乐!

👇
johnmave126:...

johnmave126 2021-10-02 03:54

std用的allocator trait是alloc::alloc::Allocator,其中支持grow,String在增加长度的时候会call grow,而allocator决定具体怎么grow。这跟libc里面的malloc/free/realloc很像,allocator在条件允许的情况下是可以直接把之前分配的内存块加长而不改变其位置的。

作者 zhongsigang 2021-10-01 23:23

改成以下代码也是一样的结果:

fn main() {
    let mut s = String::from("hello");

    let (ptr, len, cap) = (s.as_bytes(), s.len(), s.capacity());
    println!("{:p},{},{}", ptr, len, cap);

    let x = Box::new(3);
    println!("{}", x);
    
    s.push_str(" world");

    let (ptr, len, cap) = (s.as_bytes(), s.len(), s.capacity());
    println!("{:p},{},{}", ptr, len, cap);
}
1 共 7 条评论, 1 页