tl;dr:安装64位Git for Windows 2。
技术细节
0 [main] us 0 init_cheap: VirtualAlloc pointer is null, Win32 error 487
AllocationBase 0x0, BaseAddress 0x68570000, RegionSize 0x2A0000, State 0x10000
PortableGit\bin\bash.exe: *** Couldn't reserve space for cygwin's heap, Win32 error 0
此症状本身与可执行文件的映像库、损坏的Cygwin共享内存部分、dll版本冲突等无关。
Cygwin代码未能在这个固定地址0x68570000处为堆分配约5 MB的大内存块,而那里显然只有约2.5 MB的大洞可用。相关代码可以在msysgit源代码中看到。
为什么这部分地址空间不是空闲的?
原因有很多。在我的情况下,它是一些其他模块加载在一个冲突的地址:
最后一个地址大约是0x68570000 + 5 MB = 0x68C50000,但是这些wow64相关的dll从0x68810000以上加载,这阻碍了分配。
每当有一些共享DLL时,Windows通常会尝试在所有进程中以相同的虚拟地址加载它,以节省一些重定位处理。这只是一个坏运气的问题,这些系统组件以某种方式加载在一个冲突的地址。
为什么你的Git里有Cygwin ?
因为Git是一个丰富的套件,由一些低级命令和许多有用的实用程序组成,并且主要是在类unix系统上开发的。为了能够在不进行大量重写的情况下构建和运行它,它至少需要一个部分类unix的环境。
为了实现这一点,人们发明了MinGW和MSYS——它们是在Windows上以类似unix的方式开发程序的最小构建工具集。MSYS还包含一个共享库,这个MSYS -1.0.dll,它有助于在运行时解决两个平台之间的一些兼容性问题。其中的许多部分都是从Cygwin那里获得的,因为有人已经在那里解决了同样的问题。
所以不是Cygwin,而是MinGW的运行时DLL行为异常。
在Cygwin中,与MSYS 1.0相比,这段代码实际上发生了很大的变化——该文件的最后一个提交消息说“导入Cygwin 1.3.4”,这是从2001年开始的!
当前的Cygwin和MSYS的新版本(MSYS2)都已经有了不同的逻辑,希望更加健壮。它只是旧版本的Git for Windows,仍然是使用旧的破碎的MSYS系统构建的。
清洁解决方案:
Install Git for Windows 2 - it is built with the new, properly maintained MSYS2 and also has many new features, plenty of bug fixes, security improvements and so on. If at all possible, it is also recommended to use the 64-bit version. But the rebase workaround is performed automatically behind the scenes for 32-bit systems, so the chances of the problem happening there should be lower too.
Simply restarting the computer to clean the address space (loading these modules at a different random address) might work, but really, just upgrade to Git for Windows 2 to get the security fixes if nothing else.
出租汽车司机解决方案:
Changing PATH can sometimes work because there might be different versions of msys-1.0.dll in different versions of Git or other MSYS-based applications, which perhaps use different address, different size of this heap etc.
Rebasing msys-1.0.dll might be a waste of time, because 1) being a DLL, it already has relocation information and 2) "in any version of Windows OS there is no guarantee that a (...) DLL will always load at same address space" anyway (source). The only way this can help is if the msys-1.0.dll itself loads at the conflicting address it's then trying to use. Apparently that's the case sometimes, as this is what the Git for Windows guys are doing automatically on 32-bit systems.
Considering the findings above, I originally binary patched the msys-1.0.dll binary to use a different value for _cygheap_start and that resolved the problem immediately.