文章概述
内存管理对于每种开发语言来说都是一个十分重要的话题;即使像Java这种拥有“复杂”垃圾收集器的语言,也会面临GC带来的各种困扰。
C++程序设计中的很多bug也是因为内存管理不善导致的,而且难以发现和排除;如何有条理地管理内存,对于C++开发尤为重要。
“他山之石可以攻玉”,在研究如何做好C++内存管理之前,我们也可以看下其他语言是怎么做内存管理的,有什么模式或者模型能为我们所借鉴,能够更好地帮助我们理解和做好C++的内存管理。
01
不同语言的内存管理机制介绍
C/C++、Java、Objective-C/Swift 和Golang 是几种使用广泛的语言,内存管理机制也相对典型。
1.1 C/C++
C 也常被称作“可移植的汇编”,诞生之初主要是解决汇编语言的移植问题,在嵌入式和操作系统等相对底层的开发领域应用广泛;对于复杂的业务问题,因为它没有面向对象的能力(不好抽象业务逻辑),显得难以应付。
C++ 是一种很弱的面向对象的语言(模版和Interface几乎是一种相互违背的思想)。为了兼容几乎所有的C特性,背上了比较重的历史包袱。在C++11之后,这种现象有了比较大程度的改善,各种新的语言特性可以让C++开发者开发出更优雅、健壮的代码。
C语言的内存管理是典型的手动管理机制,通过malloc申请,free释放。
C++语言除了手动管理之外,还拥有弱的“垃圾回收机制”(智能指针的支持)。
C/C++中常见的内存管理问题有:
a. 数组访问越界
(Java语言可抛出ArrayIndexOutOfBoundsException)
b. 内存访问超越生命周期
‣ 栈弹出之后,依旧进行访问
(函数返回内部栈地址)
‣ 堆内存释放,依旧进行访问
c. 内存泄露 (没有释放不再使用的内存)
d. 悬空指针导致的问题
‣ 指针指向内存释放之后,指针没有复位(设置为nullptr)
‣ 使用没有复位(不为null)的无效内存(已释放或者未申请的内存)
e. C++独有的问题
‣ 非预期内的拷贝构造函数调用带来的过度复制(性能问题)
‣ 不合理的复制、拷贝构造函数的实现,导致的意外数据共享(没有设置为nocopyable)
1.2 Java
Java 是一种面向对象的现代语言,有着丰富的语言特性和开发生态。Java语言是为了实现下一代智能家电的通用系统而设计的。在借鉴C++语言的基础上,又摒弃了C++
的一些复杂特性(可能降低软件开发质量)。
比如:
a. 不允许多继承
b. 更纯粹的interface
c. 所有皆对象(基础类型除外)
d. non-static方法默认支持多态
e. 等等
不想“有心栽花花不开,无心插柳柳成荫”。
Java 在家电市场毫无起色,却因为优异的网络编程支持能力、平台无关性、垃圾回收等能力,加上恰逢互联网时代的到来,而后在企业级市场上大放异彩。
Java 因为有虚拟机的支持(先编译成字节码,由虚拟机解释成不同平台的“语言”),可以做到“一次编译,到处运行”。
Java 目前在后台开发、大数据以及App开发领域(Kotlin也是类Java语言)有着非常广泛的应用。
Java 的内存管理依托于JVM的垃圾回收器(Garbage Collections