内存耗尽点会无条件地抛出InsufficientMemoryException异常

7
我有一个非常奇怪的问题;在我们的REST应用程序中,我们引入了上传WebPackage的选项。由于解压后这些文件可能相当大,我们想通过MemoryFailPoint检查内存以确保成功的体验。
在我的本地IIS 7.5上,这个功能完美运行 - 即使是最大预期值2GB。 但是,在我们的虚拟Windows Server 2008R2 x64上,使用IIS 7.5无条件失败 - 即使只尝试1MB。
虚拟服务器托管在VMWare ESXi 4.1.0, 348481上。
以下是在虚拟机上失败的示例代码:
using (MemoryFailPoint failPoint = new MemoryFailPoint(1))
{
   ... // deliberately excluded webpackage code
}

异常信息(在XML中):

<InsufficientMemoryException>
    <Message>Insufficient memory to meet the expected demands of an operation, and this system is likely to never satisfy this request.  If this is a 32 bit system, consider booting in 3 GB mode.</Message>
    <StackTrace>
        <Line>at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes)</Line>
        <Line>at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)</Line>
        <Line>at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</Line>
    </StackTrace>
    <UserDefinedInformation>
        <failpointSizeInMegaBytes>1.00</failpointSizeInMegaBytes>
        <gcTotalMemoryBeforeInMegaBytes>153.34</gcTotalMemoryBeforeInMegaBytes>
    </UserDefinedInformation>
</InsufficientMemoryException>

我的电脑也出现了类似的异常 - 注意,在我触发异常之前,我需要分配64GB内存空间

<InsufficientMemoryException>
    <Message>Insufficient available memory to meet the expected demands of an operation at this time.  Please try again later.</Message>
    <StackTrace>
        <Line>at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes)</Line>
        <Line>at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)</Line>
        <Line>at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)</Line>
        <Line>at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</Line>
    </StackTrace>
    <UserDefinedInformation>
        <failpointSizeInMegaBytes>65536.00</failpointSizeInMegaBytes>
        <gcTotalMemoryBeforeInMegaBytes>150.63</gcTotalMemoryBeforeInMegaBytes>
    </UserDefinedInformation>
</InsufficientMemoryException>

这真的让我抓狂了,但请注意两个(非常)不同的异常:

服务器内存不足以满足操作的预期需求,而且无法满足此请求。如果这是32位系统,请考虑在3 GB模式下启动。

使用Jetbrains的dotPeek和该网站http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Runtime/MemoryFailPoint@cs/1305376/MemoryFailPoint@cs,我们可以看到可能存在的问题(尽管我仍然一头雾水):

        // Check to see that we both have enough memory on the system
        // and that we have enough room within the user section of the
        // process's address space.  Also, we need to use the GC segment
        // size, not the amount of memory the user wants to allocate.
        // Consider correcting this to reflect free memory within the GC
        // heap, and to check both the normal & large object heaps.
        ulong num1 = (ulong) sizeInMegabytes << 20;
        this._reservedMemory = num1;
        ulong size = (ulong)(Math.Ceiling((double)num1 / (double)MemoryFailPoint.GCSegmentSize) * (double)MemoryFailPoint.GCSegmentSize);
        if (size >= MemoryFailPoint.TopOfMemory)
            throw new InsufficientMemoryException(Environment.GetResourceString("InsufficientMemory_MemFailPoint_TooBig"));

本地: 当前可用内存不足以满足此操作的预期需求。请稍后再试。

编辑

Hans和我讨论了VM大小,但由于我现在已经得出结论,在开发服务器和我的机器上它更或多或少相等,所以现在我将排除这部分。欢迎任何提示、建议等。

进程信息

Process Name: w3wp
PID: 5716
User Name: NT AUTHORITY\NETWORK SERVICE
Working Set: 376.456 K
Peak Working Set: 432.400 K
Private Working Set: 320.684 K
Commit Size: 538.552 K
Handles: 919
Threads: 39

操作系统(WMI)
BootDevice: \Device\HarddiskVolume1
BuildNumber: 7601
BuildType: Multiprocessor Free
Caption: Microsoft Windows Server 2008 R2 Standard 
CodeSet: 1252
CountryCode: 45
CreationClassName: Win32_OperatingSystem
CSCreationClassName: Win32_ComputerSystem
CSDVersion: Service Pack 1
CSName: SOME_NAME [MODIFIED]
CurrentTimeZone: 120
DataExecutionPrevention_32BitApplications: True
DataExecutionPrevention_Available: True
DataExecutionPrevention_Drivers: True
DataExecutionPrevention_SupportPolicy: 3
Debug: False
Description: Development Server
Distributed: False
EncryptionLevel: 256
ForegroundApplicationBoost: 2
FreePhysicalMemory: 5.366.516 K
FreeSpaceInPagingFiles: 8.368.456 K
FreeVirtualMemory: 12.985.412 K
InstallDate: 17-01-2011 15:01:55
LargeSystemCache: null
LastBootUpTime: 11-09-2012 14:44:33
LocalDateTime: 03-10-2012 14:13:47
Locale: 0406
Manufacturer: Microsoft Corporation
MaxNumberOfProcesses: 4294967295
MaxProcessMemorySize: 8.589.934.464 K
MUILanguages: System.Object[]
Name: Microsoft Windows Server 2008 R2 Standard |C:\Windows|\Device\Harddisk0\Partition2
NumberOfLicensedUsers: null
NumberOfProcesses: 70
NumberOfUsers: 7
OperatingSystemSKU: 7
Organization: 
OSArchitecture: 64-bit
OSLanguage: 1033
OSProductSuite: 272
OSType: 18
OtherTypeDescription: null
PAEEnabled: null
PlusProductID: null
PlusVersionNumber: null
Primary: True
ProductType: 3
RegisteredUser: Windows User
SerialNumber: 11111-111-1111111-11111 [MODIFIED]
ServicePackMajorVersion: 1
ServicePackMinorVersion: 0
SizeStoredInPagingFiles: 8.388.152 K
Status: OK
SuiteMask: 272
SystemDevice: \Device\HarddiskVolume2
SystemDirectory: C:\Windows\system32
SystemDrive: C:
TotalSwapSpaceSize: null
TotalVirtualMemorySize: 16.774.452 K
TotalVisibleMemorySize: 8.388.152 K
Version: 6.1.7601
WindowsDirectory: C:\Windows

开发服务器上的VMMap截图

VMMap

由于对虚拟大小感到好奇,我从Sysinternals下载了VMMap:http://technet.microsoft.com/en-us/sysinternals/dd535533

本地机器上的VMMap截图(在此情况下MemoryFailPoint按预期工作)

VMMap 2

目前我仍不清楚为什么MemoryFailPoint失败。这一点很重要(特别是在涉及Windows Azure或其他云提供商时),需要确保有足够的内存进行操作。例如,即使是x64架构的ExtraSmall实例也会因资源非常有限而在某个过程中失败 - 这可以通过使用MemoryFailPoint来避免,从而确保数据处于有效状态。

非常感谢任何帮助。


这只是一个非常简单的测试。请记录您在Process Explorer中看到的内存使用情况,至少包括VM大小和提交大小。 - Hans Passant
无法看到非常重要的VM大小。这个问题表明asp.net积极地保留VM:https://dev59.com/questions/DkXRa4cB1Zd3GeqPwPlI - Hans Passant
如果我不使用MemoryFailPoint,则Web应用程序可以正常工作 - 即使需要消耗2GB的RAM。我下载了您推荐的Process Explorer并添加了“虚拟大小”列; 这是一个惊人的18GB。 - gimlichael
这就是导致它的原因。顺便说一下,在64位模式下运行时使用MemoryFailPoint没有什么意义。 - Hans Passant
我猜可能是某个配置问题。如果你在问题中加上 [asp.net] 标签,就能找到一些懂得这方面的 Stack Overflow 用户。 - Hans Passant
显示剩余4条评论
2个回答

5

在 .net 4.5 和 IIS 中存在一个 bug(或特性)。

MemoryFailPoint 类依赖于一个内部的与 GC 相关的变量,但在 x64 架构下,这个变量可能为 0,而除以 0 可能会生成正无穷大(double),对这个极值进行一些操作(甚至是零乘以它)可能会生成一个很大的 double 值(取决于实际的 FPU 设置)。

你可以很容易地检查这个 bug。 创建一个简单的 Web 应用程序并添加一个页面,然后粘贴以下代码:

<%
    var memoryFailPointType = typeof(System.Runtime.MemoryFailPoint);
    var staticPrivateFieldFlags = 
        System.Reflection.BindingFlags.NonPublic |
        System.Reflection.BindingFlags.Static;
    var gCSegmentSizeField = memoryFailPointType
        .GetField("GCSegmentSize", staticPrivateFieldFlags);
    var gCSegmentSize = (uint)gCSegmentSizeField.GetValue(null);
    string allocationResult;
    try
    {
        using (var mfp = new System.Runtime.MemoryFailPoint(1))
        {
            allocationResult = "Ok";
        }
    }
    catch (Exception exception)
    {
        allocationResult = exception.Message;
    }
%>
<%= gCSegmentSize %><br /> <!-- it can be 0 -->
<%= allocationResult %><br /> <!-- Insufficient memory to meet the expected demands of an operation, and this system is likely to never satisfy this request. If this is a 32 bit system, consider booting in 3 GB mode. -->

如果 gCSegmentSize 为零且 (ulong)(double.PositiveInfinite * (uint)0) 不等于 0,则不应使用 MemoryFailPoint 类。

在控制台应用程序中,FPU 控制字是 0x09001F,但在 IIS 下,FPU 控制字是 0x08001F。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接