You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中&str值内存分配位置探究及相关疑问

Rust内存分配机制探究笔记

环境信息

  • Rust版本:1.64.0
  • 系统环境:Ubuntu 22.04.1 LTS(codename jammy)

验证命令输出:

$ 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:05:25