Rust中&str值内存分配位置探究及相关疑问
Rust内存分配机制探究笔记
环境信息
- Rust版本:
1.64.0 - 系统环境:Ubuntu
22.04.1 LTS(codenamejammy)
验证命令输出:
$ rustc --version rustc 1.64.0 (a55dd71d5 2022-09-19) $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.1 LTS Release: 22.04 Codename: jammy
测试程序
编写基础Rust程序探究字符串存储逻辑:
fn tester() { let borrowed_string: &str = "world"; println!("{}", borrowed_string); } fn main() { tester(); }
编译命令(保留完整调试信息):
$ cargo build --config "profile.dev.debug=true" --config "profile.dev.opt-level=0" --profile=dev Compiling variables v0.1.0 (/home/denis/Documents/github/rust-playground/variables) Finished dev [unoptimized + debuginfo] target(s) in 0.71s
调试过程
使用rust-gdb启动调试,设置断点并运行程序:
$ rust-gdb target/debug/variables GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90 ... Reading symbols from target/debug/variables... (gdb) b 3 Breakpoint 1 at 0x7959: file src/main.rs, line 3. (gdb) r Starting program: /home/denis/Documents/github/rust-playground/variables/target/debug/variables [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, variables::tester () at src/main.rs:3 3 println!("{}", borrowed_string); (gdb) s
查看进程内存映射:
(gdb) info proc map process 6985 Mapped address spaces: Start Addr End Addr Size Offset Perms objfile 0x555555554000 0x55555555a000 0x6000 0x0 r--p /home/denis/Documents/github/rust-playground/variables/target/debug/variables 0x55555555a000 0x555555591000 0x37000 0x6000 r-xp /home/denis/Documents/github/rust-playground/variables/target/debug/variables 0x555555591000 0x55555559f000 0xe000 0x3d000 r--p /home/denis/Documents/github/rust-playground/variables/target/debug/variables 0x5555555a0000 0x5555555a3000 0x3000 0x4b000 r--p /home/denis/Documents/github/rust-playground/variables/target/debug/variables 0x5555555a3000 0x5555555a4000 0x1000 0x4e000 rw-p /home/denis/Documents/github/rust-playground/variables/target/debug/variables 0x5555555a4000 0x5555555c5000 0x21000 0x0 rw-p [heap] 0x7ffff7d5c000 0x7ffff7d5f000 0x3000 0x0 rw-p 0x7ffff7d5f000 0x7ffff7d87000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffff7d87000 0x7ffff7f1c000 0x195000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffff7f1c000 0x7ffff7f74000 0x58000 0x1bd000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffff7f74000 0x7ffff7f78000 0x4000 0x214000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffff7f78000 0x7ffff7f7a000 0x2000 0x218000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffff7f7a000 0x7ffff7f87000 0xd000 0x0 rw-p 0x7ffff7f87000 0x7ffff7f8a000 0x3000 0x0 r--p /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7ffff7f8a000 0x7ffff7fa1000 0x17000 0x3000 r-xp /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7ffff7fa1000 0x7ffff7fa5000 0x4000 0x1a000 r--p /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7ffff7fa5000 0x7ffff7fa6000 0x1000 0x1d000 r--p /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7ffff7fa6000 0x7ffff7fa7000 0x1000 0x1e000 rw-p /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7ffff7fb8000 0x7ffff7fb9000 0x1000 0x0 ---p 0x7ffff7fb9000 0x7ffff7fbd000 0x4000 0x0 rw-p 0x7ffff7fbd000 0x7ffff7fc1000 0x4000 0x0 r--p [vvar] 0x7ffff7fc1000 0x7ffff7fc3000 0x2000 0x0 r-xp [vdso] 0x7ffff7fc3000 0x7ffff7fc5000 0x2000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffff7fc5000 0x7ffff7fef000 0x2a000 0x2000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffff7fef000 0x7ffff7ffa000 0xb000 0x2c000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffff7ffb000 0x7ffff7ffd000 0x2000 0x37000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x39000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffffffde000 0x7ffffffff000 0x21000 0x0 rw-p [stack] 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 --xp [vsyscall] (gdb)
整理关键内存区域:
HEAP: [0x5555555A4000, 0x5555555C4FFF] STACK: [0x7FFFFFFDE000, 0x7FFFFFFFEFFF]
堆中查找字符串的结果
最初错误假设"world"存储在堆中,多次在堆区域执行查找:
(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world" 0x5555555a4ba0 1 pattern found. (gdb) find 0x5555555A4000, 0x5555555C4FFF, "world" 0x5555555a4be0 1 pattern found. (gdb) find 0x5555555A4000, 0x5555555C4FFF, "world" 0x5555555a4c20 1 pattern found. (gdb) find 0x5555555A4000, 0x5555555C4FFF, "world" 0x5555555a4c60 1 pattern found. (gdb) find 0x5555555A4000, 0x5555555C4FFF, "world" 0x5555555a4ca0 1 pattern found. (gdb)
每次返回的地址间隔固定为64字节:
0x5555555A4BA0 -> 0x5555555A4BE0 => 64 0x5555555A4BE0 -> 0x5555555A4C20 => 64 0x5555555A4C20 -> 0x5555555A4C60 => 64
查看字符串实际存储位置
通过调试查看变量信息,发现"world"的实际存储地址不在堆中:
(gdb) p borrowed_string $1 = "world" (gdb) ptype borrowed_string type = struct &str { data_ptr: *mut u8, length: usize, } (gdb) p borrowed_string.data_ptr $2 = (*mut u8) 0x555555591000 (gdb) x/5c 0x555555591000 0x555555591000: 119 'w' 111 'o' 114 'r' 108 'l' 100 'd'
关键地址整理:
0x5555555A4000:堆起始地址(包含)0x5555555C5000:堆结束地址(不包含),未映射内存区域起始位置0x555555591000:"world"数据实际地址0x7FFFF7D5F000:未映射内存区域结束位置
疑问解答
疑问1:为何每次扫描堆时"world"的位置都会变化?
这是因为GDB的find命令在执行过程中,会将查找相关的临时数据存储在堆中,这些临时数据里包含了要查找的"world"字符串,每次执行find都会分配新的64字节内存块(和内存分配对齐策略有关),所以返回的是不同的临时存储地址,而非原字符串的位置。
实际"world"是Rust中的字符串字面量,编译时会被放入可执行文件的只读数据段(.rodata),从内存映射看,0x555555591000属于可执行文件的r--p权限段,正是只读数据区,和堆完全无关。
疑问2:0x5555555C5000到0x7FFFF7D5F000之间的未映射内存区域是什么?
这段区域是进程地址空间中的空闲区域,属于未被操作系统分配或映射的内存。
在x86_64 Linux系统中,进程地址空间的布局是:低地址区存放可执行文件的代码、数据段和堆(堆从低地址向高地址增长);高地址区存放共享库和栈(栈从高地址向低地址增长)。中间的空闲区域留作堆的扩展空间,当程序需要更多堆内存时,操作系统会通过brk()或mmap()系统调用,将部分空闲区域映射为堆空间供程序使用,目前程序未占用这部分内存,所以显示为未映射状态。
内容的提问来源于stack exchange,提问作者Denis Beurive
相关产品推荐
相关产品推荐

