编译 50 字节代码耗费 4G 内存 2015-03-17 程序员的那些事 程序员的那些事 程序员的那些事 微信号 iProgrammer 功能介绍 最有影响力的程序员自媒体,关注程序员相关话题
2015-03-18 19:56
567 查看
编译 50 字节代码耗费 4G 内存
导读:Valve 工程师 Bruce Dawson 写了一个大小仅为50字节的程序,成功使 Visual Studio 的 C++ 编译器申请了 4GB 内存,并最终造成卡死状态。通过分析编译过程中内存的分配使用情况,Bruce 试图弄清造成这种状况的原因,并已将该问题提交给 VC++ 团队。(感谢@_La_Isla_Bonita 的热心翻译。如果其他朋友也有不错的原创或译文,可以尝试推荐给伯乐在线。)想用宏和内联汇编做些邪恶的事情(仅仅试着做一些怪异的测试,目的无关紧要),我决定写个程序让Vistual Studio的C++编译器分配4GB的内存,然后处于卡死状态。
写个50字节的代码就可以了。
一开始我可能没注意到我的机子并没有4GB的空闲内存,疯狂的数据分页,需要找到4GB的内存,使得我的笔记本在几分钟内都毫无反应。如果你的机子有超过4GB的空闲内存,使用ETW做内存分析倒是个很好的测试,看看你可以复现我的结果吗?
我简化了代码,只留下的最基本的精华部分,只是觉得这样很好玩:
void test()
{
__asm { add eax
__asm { add eax
}
这是编译器的输出:
error C2414: illegal number of operands
error C2414: illegal number of operands
error C2400: inline assembler syntax error in ‘opcode’; found ‘end of file’
fatal error C1060: compiler is out of heap space
我是在Windows 64位上运行的,编译器是32位的大型地址进程,所以堆空间耗尽意味着分配大约4GB的内存空间。我连续进行多次编译,可以看出每次4GB的内存使用量都在飙升。
我很好奇,到底是哪部分编译程序在分配这些内存。我使用cl.exe编译器,用etwheap.bat记录了所有的堆空间和VirtualAlloc 分配 ,并再次编译了源文件。事实证明我本应该使用wprui’s VAlloc Usage选项去获取追踪。仅仅只有几MB是从堆上分配的,大部分都是使用VirtualAlloc来分配的,如图所示:
接下来,为了完成调查,我查看了所有的调用栈。我们可以看到内联汇编程序的语法分析器正在使用它自己的VirtualHeap分配大量的Asm Tokens。VirtualHeap::Create 预留内存空间,VirtualHeap::HeapExtend提交内存。再深入研究下(没有显示)发现内存空间预留在512KB的内存块,被提交在32KB的内存块。
还有一些细节,不是很清楚,像为什么VirtualHeap::HeapExtend调用 VirtualHeap::Create,但是却没有源代码,难以得知。
所以我们不再探究了,我像往常一样将把这个问题提交给VC++团队。如果他们解决了这个问题,我并不惊讶,这也算不上是一个严重的问题。第一次遇到这个问题时,因为我的机子没有4GB的空闲内存,所以才注意到它。
编译器是32位进程也是件好事儿,要不然它还会继续消耗内存,将远远超过4GB。条件限制万岁!
这些测试都是在VC++ 2010 的调试版本上进行的,我没试过其他版本。
Linux 变体
还有一个很类似的问题(链接器在一个很简单的程序上消耗了大量内存,详情见 栈溢出)。
Windows 糟透了?
我预料到有人会说Windows太烂了,这就是为什么当遇到这个问题时,我的笔记本几分钟内都毫无反应。但是如果在Linux和OSX系统上分配(或写入)4GB的内存,并不会引发严重的系统延迟问题,但其实这说明不了什么。我的笔记本只有8GB的内存,大部分都在被使用,想获取到空闲的4GB内存的唯一可能的办法就是把大量的数据写到磁盘上。笔记本的硬盘相当慢,如果我是在工作机子上(32GB的内存,20GB可用)或者当笔记本上只有很少的程序在运行时(5GB空闲内存),做同样的测试,4GB内存的分配和释放不到5秒中就可以完成。
Reddit的讨论链接在这儿。
额外补充
很奇怪,怎么会有一些博客文章比其他人的更受欢迎…
有人在复现这个问题时遇到了困难,这个bug只能确定在VS2010 SP1 出现,并且test函数放在源文件的最后。
这显然不是一个严重的bug—代码也有缺陷,编译器有给出了警告并且指出问题所在,也没出现什么大问题。但是它的确是一个失败的词法分析程序。尤其是,内存不足会阻止VC++去报告一些括号不匹配的问题—假如你在test函数之后再添加一个函数,词法分析完成后,额外的警告就会显示出来了。
编译错误代码时给出出错的信息提示是很重要的,这也是Clang的显式设计目标之一。
PS:【@棉花糖啊棉花糖o_o】成功在 Visual Studio SP1 中复现这个 Bug 了。见下图或查看原图。
原文出处:Bruce Dawson
译文出处:伯乐在线 - JingerJoe
链接:http://blog.jobbole.com/46106/
相关文章推荐
- 编译 50 字节代码耗费 4G 内存
- 整理一下Java动态编译Java代码,并在加载到内存中然后执行类中方法的api的介绍
- 请编写实现malloc()内存分配函数功能一样的代码。给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
- 今日内容介绍 1、自定义类型的定义及使用 2、自定义类的内存图 3、ArrayList集合的基本功能 4、随机点名器案例及库存案例代码优化 ###01引用数据类型_类 * A: 数据类型
- 虚函数的特性--内存结构的研究, 对象有虚表的反而多占内存了(多4字节即一个指针),函数是预告编译在代码区
- 1 开发一个注重性能的JDBC应用程序不是一件容易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告诉你。 本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的原则已经被许多现有的JDBC应用程序编译运行并验证过。 这些指导原则包括: 正确的使用数据库MetaData方法 只获取需要的数据 选用最佳性能的功能 管理连
- 4G相关标准UMB/LTE/WiMAX介绍
- 【Java】Javascript操作IE相关功能的代码
- 无需编写Java代码就能生成增删改查功能的CZTZ-JavaEE平台介绍
- ASP.NET Whidbey 中新的代码编译功能
- VC6功能增强插件TabBars代码编译和调试说明
- ASP.NET Whidbey 中新的代码编译功能
- Apache内存管理-内存池介绍与内存结点介绍(参照网络上其他资料与代码全景分析)
- 内存字节对齐---代码实现(自己整理的,这是一切字节对齐最本质的东西)
- 不做只会code的程序员——关注代码之外的事情
- JVM学习笔记(1、 基本结构;2、Java代码编译和执行的整个过程3、内存管理和垃圾回收 4、 内存调优 )
- aspnet_regsql在哪里 怎么运行它 以及功能介绍-asp.net关注
- 我们——程序员应该关注,功能?设计?
- 使用 lex 和 yacc 编译代码,第 1 部分:介绍
- Dreamweaver CS4新增相关功能介绍