任何一个程序都需要内存管理,不同语言一样的是, 内存管理的细节掌握在谁的手里

C 时代编译器水平,要程序员掌握内存管理细节。虽获得最大灵活性, 但手工管理不好管控,容易出瑕疵

编译器技术发展, 从程序员手中接管内存管理任务

如何接管内存管理分两派

  • 垃圾收集
  • RAII (Resource Acquisition Is Initialization) + 智能指针

引子

有GC语言, 程序员只管分配内存, 一般无需操心释放。垃圾收集器间歇性运作, 释放无用内存

许多语言里编译器不实现垃圾收集,在语言附带的 runtime 实现

编译器为运行时提供附加的信息,导致语言和运行时的强耦合,让人无法分清语言的特性和运行时的特性

gc 弱点

一、保证不了没内存泄漏

野指针是没了, 只要对象还有引用就不会被释放(java还有强软弱虚4种引用)

gc 语言或多或少都废除了指针, 用引用替代了指针

很多复杂原因会导致gc无法回收特定的内存(内存泄漏)

到底是延迟收集策略还是真的发生了内存泄漏 ? 永远都无法正确分辨

结果程序员最终不得不回到 C 语言的老路上, 小心的检查所有的内存分配, 确保没有触发垃圾收集器的bug或者特定的一些策略

几乎所有使用带垃圾收集的语言开发的程序, 在其开发的后期都要经历惨痛的 “内存检查” , 回顾所有可能导致内存泄漏的代码

二、只能回收内存,其他资源, 如消息列队, 文件描述符, 共享内存, 锁等其他资源还要手工管理, 像 C 程序员那样小心翼翼的操作

最终垃圾收集仍然没有解决 “人容易犯错” 的问题,还是把其他资源的泄漏问题丢给了程序员

C++ 世界

C 派因为喜欢 “自己控制一切” (天生的 M 属性)

C++ 同样认为要把程序员从资源管理的重担里解放出来,做了很多思考, 资源管理逐步发展,终经 30年找到解决的办法(智能指针), 写入了 C++11 标准,对比”投机取巧” 的 GC派(java)

C++ 要想实现 RAII + 智能指针, 两大技术缺一不可

  • 自动确定并调用 构造函数和析构函数
  • 模板

第一步试图解放资源管理重任, 加入构造和析构函数,编译器总是保证, 生命期终止(return 返回 or 抛出异常)的对象一定会被调用析构函数

编译器自动保证对象生命期 技术依托下, RAII 将资源的管理变成对象的管理

自动变量 (创建在栈上的对象, 类的成员变量) 生命期由编译器保证, 只要在构造函数里申请资源, 在析构函数里正确释放资源, 解决一大部分资源管理问题

模板使 RAII 技术得以 “一次实现到处使用”

但如果对象分配于堆上, 还是得手工调用 delete 删除对象,否则还是泄露了

智能指针

std::auto_ptr 效果不好, 不是所有指针都可以为 auto_ptr 所代替。最要命的是, STL 容器无法使用 auto_ptr

std::shared_ptr 将需要程序员管理的堆对象也变成了能利用编译器自动管理的自动变量

彻底的将 delete 关键字变成了 shared_ptr 才能使用的内部技术

编译器能自动删除 shared_ptr 对象, 也就是编译器能自动的发出 delete 调用

没模板的话要用 RAII 实现智能指针只能利用 “单根继承” (如MFC 诞生在模板还没有加入 C++ 的年代)

发明出编译器实现的自动内存管理需要时间, C++ 花了 30年的时间

没有耐心的语言走了捷径, GC 就是这条捷径

一些评论

语言发展是一个实践+平衡的过程 实践才能知道设计的功能别人是会怎么用,实际效果如何 平衡才能让大家都接收认可并共同执行这一标准

c++ 为实现提供了很多支持,但设计中很多oop的支持,在实际应用中最终几乎成了噩梦,感觉永远无法达到完美或实现理想

“人容易犯错”,无法保证团队中每个人都能充分理解C++那庞大琐碎的OOP实现,无法保证每个人都小心使用每个看起来很炫的语法糖

gc 有效缓解了开发人员水平不一致的问题,随着gc技术的发展,人们似乎找到了灵丹妙药

它看起来很美好,但仍然无法彻底解决资源管理问题

为了避免那隐蔽的泄漏,不得不去了解gc的实现底层,在开发时,无意中仍然有可能引发资源的长期占用得不了释放的问题

而且gc的研究似乎进入到了一个瓶颈,无法再有大的发展了,所能进步的也只是少许改良

C/C++中直接操作内存出现的问题如,内存泄漏,访问越界,一般都是可以根除的,即使没有根除,也可以不断的FIX中接近于没有内存问题

GC语言在语言层回避内存管理问题,丢给jvm处理,因为GC的不确定性,所以GC中出现的问题你基本无法解决,任何的FIX只是止痛药式的补丁,不能从根本上解决GC带来的问题

内存管理是”本质复杂的问题”,最好的解决办法是直接面对(也就是手动管理内存),这样的可控性和灵活性更高