【Rust 中级教程】 07 内存

简介: 【Rust 中级教程】 07 内存

0x00 开篇


关于 trait 的相关文章暂告一段落,本篇文章将向大家介绍下一个计算机的通用概念——内存。如果你是计算机相关专业的读者或者对内存比较了解,那可以略过本篇文章。


0x01 物理内存与虚拟内存


内存是计算机一个重要的概念,不同的语言对内存的管理不同,有手动管理内存和自动管理内存的两种方式,这也就导致不同语言直接的性能不同。早期的计算机程序都是直接跑在物理内存上的,这就要求程序大小不能超过物理内存的上限。现代计算机为提高CPU使用率、并发执行多个程序、隔离程序间内存地址、打破物理内存上限,采用虚拟内存机制来运行程序。


物理内存就是我们常说的内存条。而虚拟内存仅仅是一个概念。对于任何一个程序来说,它包含了程序运行时可用的内存空间总和,包括共享的、非共享的、物理内存中的、存在分页中的等等。简单点说:虚拟内存映射 = 物理空间 + 硬盘空间 + 未使用的映射。


虚拟内存大小组成的地址空间就是虚拟内存空间,虚拟内存空间是有地址空间的。这就引出另一个概念——虚拟地址空间。虚拟地址空间是线性的,我们所接触的地址都是虚拟地址。虚拟地址空间又被划分为内核空间和用户空间。以 4G 内存为例,在 Windows 32位操作系统中默认内核空间和用户空间的比例是1:1 (2GB的内核空间,2GB的用户空间),而在32位Linux系统中默认的比例是1:3 (1GB的内核空间,3GB的用户空间)寻址范围是 0x00000000 - 0xFFFFFFFF。讲了这么多,下面直接看图吧。


0a2653c851af460fa595bd959398a8f1.png


细心的读者可能发现了,Windows 的内核空间有 64KB 没有使用。从 0x7FFF0000-0x7FFFFFFF:这个64KB是禁止访问的,因为它挨着内核区域,防止内核区域被覆盖,以破坏内核的正确性和完整性,如果试图改写此区域,会产生异常。


最后我们以Linux系统为例,看下 Linux 系统下的虚拟地址空间示意图。Linux 对进程地址空间有个标准布局,地址空间中由各个不同的内存段组成,主要的内存段如下:


  • 程序段 (Text Segment):可执行文件代码的内存映射
  • 数据段 (Data Segment):可执行文件的已初始化全局变量的内存映射
  • BSS段 (BSS Segment):未初始化的全局变量或者静态变量(用零页初始化)
  • 堆区 (Heap) : 存储动态内存分配,匿名的内存映射
  • 栈区 (Stack) : 进程用户空间栈,由编译器自动分配释放,存放函数的参数值、局部变量的值等
  • 映射段(Memory Mapping Segment):任何内存映射文件


2d65d23f6d4748949b924e4057485923.png


0x02 栈内存


如果你了解过数据结构,那你肯定听说过“栈”,它拥有后进先出的特性。栈内存则拥同栈数据结构一样的特性,支持入栈和出栈。下图是栈数据结构入栈和出栈的示意图。


6de278e6d6694ce5bb08e7e842b7e74b.png


当有数据入栈时,栈顶的地址会变小,反之数据出栈时,栈顶的地址会增大。栈内存的主要作用是在程序运行时保存函数调用所维护的一些信息。有关栈内存相关的知识点,大家可以通过搜索或者其他书籍了解下,我就不再展开讲解了。


0x03 堆内存


同样,“堆”也存在于数据结构中,它是一颗完全二叉树(有关数据结构相关的知识,后面也会有一系列的文章,这里不再多余赘述)。我们主要了解堆内存。堆是一个大块的内存空间,程序申请堆空间时是大小不一,无序且不连续。这就出现一个如何管理堆内存的问题。Rust编译器目前自带两个默认分配器:alloc_systemalloc_jemalloc。从 Rust 1.32.0 版本开始,把分配器默认切换为  alloc_system ,但开发者仍然可以从 crate 使用到 alloc_jemalloc。过去Rust使用alloc_jemalloc,虽然普遍认为alloc_jemalloc的性能比较好,但随着时间发展,情况已经改变。


0x04 堆内存和栈内存的区别


其实物理内存并没有区分堆和栈,堆内存和栈内存只存在于虚拟地址空间中。

  • 生命周期
    在栈内存中保存的数据往往生命周期都比较短。比如某个函数执行结束后,内存会释放。但是如果想要长久的保存数据,这时候需要使用堆。
  • 是否能够确定数据大小

在写代码时,如果可以知道某种数据类型的大小,我们可以将其分配到栈中,如固定大小的基本数据类型和固定大小的数组。但是如 果是动态数组则需求分配到堆内存。

  • 速度
    数据在入栈出栈操作要比在堆上分配内存或者在堆上读取数据的速度快。因为入栈出栈操作,位置总是在栈顶。而在堆上操作时要做很多工作。

开发过程中,我们只能通过指针来掌握已分配的堆内存,如果指针指向的堆内存已经释放,但是指针还没有释放(野指针),这就会对我们的程序造成严重的影响。


0x05 小结


本篇文章介绍了很多概念,但是我也只是简单写一下,如果你对内存方面的知识比较感兴趣,可以多多查找资料。我相信很多人接触 Rust 是因为很多文章都在说 Rust 很安全。那么 Rust 为何安全?Rust 为什么能保证内存安全呢?Rust 是如何管理内存的?和其它主流语言的区别是什么呢?有关这些疑问,接下来的文章将会全部给你答案。

相关文章
|
1月前
|
存储 Rust 安全
Rust 中的动态内存分配
【10月更文挑战第10天】在Rust中,动态内存分配主要用于运行时按需分配内存,与静态分配不同,它能处理大小不确定的数据结构。Rust通过`Box`类型实现堆分配,`Vec`类型则用于动态数组,两者均内置智能内存管理。`Rc`和`Arc`提供引用计数机制,支持数据共享并确保内存安全。Rust的内存安全管理机制有效避免了悬空指针和双重释放等问题。
|
1月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
|
1月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
51 0
|
3月前
|
数据采集 Rust 安全
Rust在网络爬虫中的应用与实践:探索内存安全与并发处理的奥秘
【8月更文挑战第31天】网络爬虫是自动化程序,用于从互联网抓取数据。随着互联网的发展,构建高效、安全的爬虫成为热点。Rust语言凭借内存安全和高性能特点,在此领域展现出巨大潜力。本文探讨Rust如何通过所有权、借用及生命周期机制保障内存安全;利用`async/await`模型和`tokio`运行时处理并发请求;借助WebAssembly技术处理动态内容;并使用`reqwest`和`js-sys`库解析CSS和JavaScript,确保代码的安全性和可维护性。未来,Rust将在网络爬虫领域扮演更重要角色。
77 1
|
3月前
|
Rust 网络协议 安全
揭开Rust网络编程的神秘面纱:全新的Socket体验,让你告别内存泄漏的噩梦!
【8月更文挑战第31天】Rust语言凭借其卓越的内存安全性和高性能,在网络编程领域展现出独特优势。本文将带你探索Rust中的Socket编程,展示如何使用标准库`std::net`模块轻松实现TCP服务器与客户端。通过简洁的代码示例,你将看到Rust如何简化网络通信流程,并通过`async/await`异步模型高效处理并发连接。此外,Rust社区提供的优秀库如`tokio`和`async-std`进一步增强了异步网络编程的能力。无论是从基础示例还是高级应用,Rust都将为你带来耳目一新的网络编程体验。
265 0
|
3月前
|
Rust 安全 程序员
揭秘Rust语言的内存安全秘籍:如何构建坚不可摧的系统级应用?
【8月更文挑战第31天】Rust语言凭借其独特内存安全机制在编程领域脱颖而出,通过所有权、借用与生命周期等概念,在保证高性能的同时避免了缓冲区溢出等常见错误。本文深入探讨Rust的内存安全机制,并通过示例代码展示如何利用这些机制构建高效且可靠的系统。尽管这些机制增加了学习难度,但为软件开发奠定了坚实基础,使Rust成为系统、嵌入式及网络编程的理想选择。随着社区的发展,Rust将在未来软件开发中扮演更重要角色。
84 0
|
4月前
|
Rust Java C++
Rust 问题之内存泄漏如何解决
Rust 问题之内存泄漏如何解决
|
4月前
|
JavaScript Java 开发者
Rust 问题之在众多编程语言中关于内存管理有哪些分类
Rust 问题之在众多编程语言中关于内存管理有哪些分类
|
4月前
|
存储 Rust JavaScript
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
|
5月前
|
Rust 安全 开发者
探索Rust语言的内存安全特性
【6月更文挑战第8天】Rust语言针对内存安全问题提供了创新解决方案,包括所有权系统、借用规则和生命周期参数。所有权系统确保值与其所有者绑定,防止内存泄漏;借用规则保证同一时间只有一个可变引用或多个不可变引用,消除数据竞争和野指针;生命周期参数则强化了引用的有效范围,提升安全性。通过这些特性,Rust帮助开发者编写出更健壮、安全的高性能软件,有望成为系统编程领域的领头羊。