从Rust看现代编程语言内存管理新思路(一)

达芬奇密码2018-08-13 16:22

编程语言内存管理的挑战

内存管理一直以来都是编程的核心问题,编程语言由于被设计用于的场景不同选择了不同的内存管理策略,作为系统级编程语言的C/C++为精确控制内存的Layout以及数据结构的生命周期提供了手动控制内存申请、释放的接口,我们将这类语言称为非GC语言,而以Java为代表的适用于业务领域的编程语言则选择为开发者减负提供了垃圾收集器自动回收申请的内存,这类语言我们称为GC语言。但面对长期编程实践的挑战,这两种内存管理的策略都遭遇到了种种问题。

GC与非GC语言的困境

首先非GC语言的问题程序员都是熟知的,手动释放内存带来了内存泄漏的隐患,重复释放内存/生命周期控制bug等问题又带来了悬挂指针等问题,随之而来的是一次次的程序奔溃,并且随着一个项目同时参与编程的人员增加,某一位程序员利用了一些“编程技巧”便可能造成整个项目的不稳定,调bug的耗时往往超过写程序的时间,这一切导致程序员不敢轻易使用C/C++来实现大型项目,一方面也造就了Java的盛行。

即使采用垃圾收集的内存管理策略,问题却远未解决,垃圾收集器解决了内存管理问题却带来了性能的负担,JVM的垃圾收集器设计精巧实现复杂,但实际使用中当我们的大吞吐量服务程序使用标准的垃圾收集器则一定会遇到Full GC停顿的问题,通常4GB的堆可能Full GC时间便会超过1s,如转而使用CMS垃圾收集器停顿时间减少但吞吐量也随之下降,Major GC耗时也经常要上百毫秒,并且长时间运行后还是可能会冷不防来一次Full GC耗上几秒,垃圾收集的性能负担使得采用垃圾收集的程序无法达到软实时的特性,这尤其在GUI程序开发方面会带来用户体验的问题。为应对垃圾收集停顿的问题一些语言引入了依靠引用计数进行垃圾收集,例如Objective-c支持自动引用计数,但引用计数垃圾收集的计数操作会带来大量额外性能开销,并且对于并发程序环境计数的性能影响更大,因此往往只用于GUI开发场景,而很少用于需要大吞吐量的服务器端编程场景。
垃圾收集的问题还不限于性能,GC语言需要依托运行时,这使得语言之间的高效调用被运行时这条鸿沟阻断。我们在开发中经常会碰到的场景是需要将C/C++实现的库嵌入到GC语言开发的程序中去,以解决一些对性能、代码安全要求较高的场景或是需要和OS、硬件打交道的场景,这时我们会遇到两方面的问题,首先考虑数据结构在GC语言运行时中创建,理论上我们可以将引用直接交给C程序,但C程序要访问这一数据结构就必须理解GC语言中创建的这一数据结构的内存Layout,但这需要强烈的依赖GC语言运行时的实现,这也导致了很难解决这问题,即使有时候C程序无需理解该数据结构只需要持有,但这同样会有问题,这是由于GC语言的运行时并无法了解C程序对其内存引用的情况,因此只要将引用传递给了C程序除非C程序显式释放引用否则只能在堆中将该引用对应的内存pin住,但这样会给垃圾收集器带来负担从而影响性能,所以当跨语言调用时往往选择进行实例的拷贝而不是引用。

说了那么多问题,现在我们来看一下一门理想中的高性能计算机编程语言在内存管理方面我们会希望具有什么特性:

  • 能够自动控制内存的申请和释放
  • 程序运行能够做到软实时
  • 内存管理带来的性能损耗影响轻微
  • 数据结构在内存中的Layout能够与C/C++兼容
  • 能够优雅处理跨越语言边界时对象的引用关系

既然有需求存在便一定会有人尝试去解决,即使目前还没有真正获得认可能解决一揽子问题的计算机语言,但我们已经开始看到了一些希望。下面我们就来看一门新兴的计算机语言Rust是怎样来应对内存管理问题的。

Rust背景简介

Rust语言创始于2006年,当时只是Moziila雇员Graydon Hoare的个人项目,之后Mozilla对于这一项目产生了兴趣从而专门成立了团队来开发这一新语言,并用于实验性的并行浏览器引擎Servo项目。目前Rust项目在Github上Star数超过7000,社区非常活跃。

一方面Rust是一门集合了现在流行的编程语言特征的语言,你会从Rust的语法中看到C++,Erlang,Haskell的影子,另一方面Rust标榜着自身在性能、安全性方面有着独到的特点。Rust的Introduction开篇第一句话:

Rust is a modern systems programming language focusing on safety and speed. It accomplishes these goals by being memory safe without using garbage collection.

我们来看一下Rust语言的一些基本特征:


  • Rust是一门静态类型编程语言,具备严格的类型系统,支持泛型,它的泛型实现采用的是类同C++语言的模式,在编译时展开,编译器支持类型推断,这也使得编写程序时不用反复敲类型名称。

  • Rust是一门面向对象的语言,通过Traits作接口的约束。

  • Rust的运行时是可选的,运行时对Rust语言来说只是std库,这点和C/C++非常类似,这点也使得Rust语言和Java、golang之类语言不同,可以直接用来作为C/C++的替代,理论上甚至可以拿来编写操作系统内核,之前偶然看到美国弗吉尼亚大学计算机OS课程的作业便要求是用Rust语言来完成的。在不带运行时的情况下Rust内存管理虽然是自动的但并不依赖垃圾收集器,这也是本文后续要介绍的。

  • 处理并发问题Rust采用的是类似Erlang的基于消息传递的Actor模型,从而避免Data Races的问题

  • Rust支持Lambda、Pattern Matching等一些具有函数式风格的语法

  • Rust的编译基于LLVM,为可执行代码提供了很高的执行效率保障


Rust语言目前的版本为0.12,预计2015年上半年发布1.0版本,届时很可能会成为一门重要的 System Programming 语言。


可以看到Rust语言可谓集成了不少时髦的特性,当然Rust语言不仅仅是时髦而已,在下一篇从Rust看现代编程语言内存管理新思路(二) 将为大家揭开Rust语言内存管理的秘密。


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者陈谔授权发布。