从程序员的角度去看为什么现在的电脑应用程序这么吃内存(RAM)?

文章声明,本篇文章题材源自知乎的问答 为什么现在的电脑应用程序这么吃内存(RAM)?


 

别看程序员平时一个个自黑,黑得很嗨。但是当真正面对自己工作中的黑点时,大家都不提了。一个个都辩解说:内存不用就是浪费——但问题是,在大多数场景下,那些内存里存的数据,恐怕大都是0。

大多数程序使用内存暴涨的锅,就是现在大多数程序员的水平、责任心、工作环境造成的。

 

我举一些例子:

  1. 现在很多程序员,在实际工作中,已经几乎没有精细化设计数据结构的能力了。例如说,有多少人在最近两三年的工作中,使用到了bitfield?哪怕有用到的,大多也是因为要沿用一些已经设计好的底层古老协议而不得不用。当然,位操作这个锅主要是C/C++程序员背的。但是其他程序员也不见得好到哪去:有多少程序员自己在生产项目中,设计过一个除哈希表和链表外,其他任意一款数据结构及其变形结构的?说得难听点,大多数脚本系语言的程序员,实际生产中,连基本的二叉树之类都没用过。
  2. 太依赖于哈希表+双链表这种包打天下的组合了。我知道,这个组合确实很简单很快捷。但是缺点就是本问题:内存消耗大。如果说脚本语言的程序员,确实有时候没别的现成的数据结构可用,情有可原的话,那么最近几年,不少C/C++/JAVA的程序员,也逐渐染上了这个毛病。
  3. 文本化协议泛滥。传统上,数据协议都是二进制的。对于计算机来说,二进制是最友好而且也冗余最小效率最高的协议。但是因为对人的识别度不好,尤其是web包打天下的潮流开始,现在很多系统,无论是网络协议还是存储协议,都基本上是文本化(http/html/json/xml……)的了。最后一个比较流行而且设计比较精巧的二进制协议,我觉得就是protocol buffers了。而且,这个锅是会传染的,一旦某个系统协议用了文本化之后,所有接入这个系统的协议,都必须都弄一套这样的文本解析模块。
  4. 代码脚本化/动态化语言泛滥(包括解释器机制的泛滥)。这个锅主要来自于解释器引擎,因为在脚本语言里,定义一个变量,基本上都不会出现char/short这类节约内存数据类型的,最最起码都是上来就给你个int。而且这个int占用的内存实际上往往还不仅仅是4个字节,而是这样的:
union DATA
{
    int bool_val;
    intptr_t int_val;
    double double_val;
    char* str_val;
    XXX* obj_val;
};

可以看到,64位系统中,很多脚本解释器里,一个变量最最起码占用的也是一个指针的宽度(8个字节)。如果有额外的引用计数、动态类型标识符之类的话,还要再往上加。

 

最后一条:GC。这点大家都知道,我就不废话了。

 

所以,很多答案都说了:浏览器很吃内存。回头看看,浏览器里跑的东西,命中了上面几条?

能不吃内存吗?


随便补充几条在评论中大量反复出现的辩解来回复一下:

1:在代码设计时就考虑了数据结构/内存布局,并不意味着大量的额外工作量。

事实上,得益于各种库/扩展,大部分常见的数据结构,都有现成的模块可用。实际上,大多数情况下,数据结构/数据模型都是会单独封装/抽象出一个库或者模块的。所以,实际工作量无非也就是调A库还是调B库,可能也就内部接口api换了换而已,少数情况下这些api差异可能会少量穿透到功能代码中。

至于说没有合理抽象分层封装的项目,所有功能代码、数据操作代码、还有其他的什么乱七八糟的代码都混在一起?小项目无所谓,就那三五条枪,说改就改,不改也无所谓,反正说不定什么时候就死了。大项目?早死早超生吧。

2:写出高质量的代码并不代表项目研发速度降低——尤其当你的项目烂到一定程度需要大面积重构的话,把这些重构的时间和人力分开计算到平时的每个需求迭代中,只要有心做好,代码都不至于被评价为“烂”。大部分985211科班出来的程序员,这点能力我还是相信有的。

3:项目代码烂,说真的,主要还是在没有设计规划时,为了一昧的赶进度而反复堆砌功能代码,尤其是到处的代码拷贝。事实上,在接需求动手前,设计和规划代码的工作,并不会耽误开发进度,而且能让代码质量大幅提高——这在我经历过的很多项目中都能体现出来。

4:至于bitfield这种东东,我也没让各位在项目中到处用——我也反对过早优化。在很多时候,能把pb用好就很不错了。就说一个现实的例子,在某项目的mc,由原来各种乱七八糟的文本数据往里丢,统一改为pb格式后,同样的机器数目,缓存数据条数增加了30%以上。光是这一改就减少了多少cache miss,减少了多少后端数据库的压力?

5:实际上,程序/指令/资源/栈等占用的内存是很有限的。大多数内存,都是用于cache的——无非是谁用,用来干什么而已。相比于访问cache数据的那点增删改查的时间,cache miss导致访问主存(文件/网络/数据库)才是消耗的大头。所以,内存布局设计得好,合理使用数据结构,空间利用率高,在大多数时候直接体现就是访问主存的频度降低,然后就是性能,尤其是响应速度的提高。

 

最后说点务虚的东西:

说白了,编程这种工作,本质上就是一种工程学。既然是工程学,那就是要碰到实际问题时如何根据各种条件寻求优化解。这实际上是一种经验积累而来的技能,说难不难说易不易:只有大量练习,熟能生巧这一条路——跟大家在高考前大量的题海战术是一回事。

所以,以前在学校里学了下,平时以各种借口不用不思考不积累,等你真的要用想用的时候,用不用得起来,出不出洋相,还真的要看运气了。那么这些程序员,不管一开始的起点是高是低,反正一辈子的天花板,伸伸手就能够得着了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章