woj*_*ojo 12 heap winapi memory-management windows-vista windows-7
我试图追踪Windows Vista和Windows 7中堆内存功能的巨大减速(我没有在任何服务器版本上测试).它根本不会发生在Windows XP上,只发生在微软较新的操作系统上.
我最初使用Windows上的PHP编译遇到了这个问题.脚本本身似乎以预期的速度运行,但在脚本执行后,我在内部PHP关闭功能中遇到了1-2秒的延迟.在启动调试后,我发现它与PHP内存管理器使用HeapAlloc/ HeapFree/有关HeapReAlloc.
我追溯到HEAP_NO_SERIALIZE堆函数上使用标志:
#ifdef ZEND_WIN32
#define ZEND_DO_MALLOC(size) (AG(memory_heap) ? HeapAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, size) : malloc(size))
#define ZEND_DO_FREE(ptr) (AG(memory_heap) ? HeapFree(AG(memory_heap), HEAP_NO_SERIALIZE, ptr) : free(ptr))
#define ZEND_DO_REALLOC(ptr, size) (AG(memory_heap) ? HeapReAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, ptr, size) : realloc(ptr, size))
#else
#define ZEND_DO_MALLOC(size) malloc(size)
#define ZEND_DO_FREE(ptr) free(ptr)
#define ZEND_DO_REALLOC(ptr, size) realloc(ptr, size)
#endif
Run Code Online (Sandbox Code Playgroud)
和(这实际上是设置为默认HeapAlloc/ HeapFree/ HeapReAlloc在函数)start_memory_manager:
#ifdef ZEND_WIN32
AG(memory_heap) = HeapCreate(HEAP_NO_SERIALIZE, 256*1024, 0);
#endif
Run Code Online (Sandbox Code Playgroud)
我删除了HEAP_NO_SERIALIZE参数(替换为0),它修复了问题.脚本现在可以在CLI和SAPI Apache 2版本中快速清理.这是针对PHP 4.4.9的,但PHP 5和6源代码(开发中)在调用中包含相同的标志.
我不确定我做的是不是很危险.它都是PHP内存管理器的一部分,所以我将不得不做一些挖掘和研究,但这提出了一个问题:
为什么Windows Vista和Windows 7上的堆内存功能如此之慢HEAP_NO_SERIALIZE?
在研究这个问题时,我提出了一个很好的打击.请阅读博客文章http://www.brainfarter.net/?p=69,其中海报解释了问题,并提供了一个测试用例(包括源代码和二进制代码)以突出显示该问题.
我在Windows 7 x64四核8 GB机器上的测试给出了43,436.哎哟! 没有HEAP_NO_SERIALIZE标志的相同结果是655,在我的情况下快70倍.
最后,使用malloc/ free或new/ 使用Visual C++ 6创建的任何程序delete似乎都会在这些较新的平台上受到影响.默认情况下,Visual C++ 2008编译器不会为这些函数/运算符设置此标志,因此它们不会受到影响 - 但这仍然会影响很多程序!
我鼓励您下载概念证明并尝试一下.这个问题解释了为什么我在Windows安装上的普通PHP是爬行的,并且可以解释为什么Windows Vista和Windows 7有时会变慢.
更新2010-01-26:我收到了微软的回复,声称低碎片堆(LFH)是堆的实际默认策略,可以保存任意数量的分配.在Windows Vista中,他们重新组织了大量代码,以删除不再是处理堆API调用的常见情况的额外数据结构和代码路径.使用HEAP_NO_SERIALIZE标志并在某些调试情况下,它们不允许使用LFH,并且我们会陷入通过堆管理器的缓慢且不太优化的路径上.因此......强烈建议不要使用,HEAP_NO_SERIALIZE因为您将错过LFH的所有工作以及Windows堆API中的任何未来工作.
Ale*_*ski 11
我注意到的第一个区别是Windows Vista 总是使用低碎片堆(LFH).Windows XP似乎没有.RtlFreeHeap因此,在Windows Vista中缩短了很多时间 - 所有工作都委托给了RtlpLowFragHeapFree.有关LFH及其在各种操作系统中的存在的更多信息.请注意顶部的红色警告.
带有修补程序KB 816542的Windows XP,Windows Server 2003和Windows 2000:
后备列表是一种快速内存分配机制,仅包含固定大小的块.对于支持它们的堆,默认情况下会启用旁视列表.从Windows Vista开始,不使用后备列表,默认情况下启用LFH.
另一个重要的信息:LFH和NO_SERIALIZE互相排斥(两者都不能同时激活).结合
从Windows Vista开始,不使用后备列表
这意味着NO_SERIALIZEWindows Vista 中的设置会禁用LFH,但它不会(也不会)回退到标准的后备列表(作为快速替换),根据上面的引用.我不清楚Windows Vista在NO_SERIALIZE指定时使用的堆分配策略.根据其性能,看起来它正在使用可怕的天真.
看几个堆栈快照allocspeed.exe,似乎总是处于Ready状态(不是Running或Wait),而是来自HeapFree的TryEnterCriticalSection,并将CPU挂起接近100%负载40秒.(在Windows Vista上.)
示例快照:
ntdll.dll!RtlInterlockedPushEntrySList+0xe8
ntdll.dll!RtlTryEnterCriticalSection+0x33b
kernel32.dll!HeapFree+0x14
allocspeed.EXE+0x11ad
allocspeed.EXE+0x1e15
kernel32.dll!BaseThreadInitThunk+0x12
ntdll.dll!LdrInitializeThunk+0x4d
Run Code Online (Sandbox Code Playgroud)
这很奇怪,因为NO_SERIALIZE正好告诉它跳过锁定获取.有些东西没有加起来.
这是一个只有Raymond Chen或Mark Russinovich才能回答的问题:)