OutOfMemoryException 类

定义

当内存不足以继续执行程序时引发的异常。

public ref class OutOfMemoryException : Exception
public ref class OutOfMemoryException : SystemException
public class OutOfMemoryException : Exception
public class OutOfMemoryException : SystemException
[System.Serializable]
public class OutOfMemoryException : SystemException
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public class OutOfMemoryException : SystemException
type OutOfMemoryException = class
    inherit Exception
type OutOfMemoryException = class
    inherit SystemException
[<System.Serializable>]
type OutOfMemoryException = class
    inherit SystemException
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type OutOfMemoryException = class
    inherit SystemException
Public Class OutOfMemoryException
Inherits Exception
Public Class OutOfMemoryException
Inherits SystemException
继承
OutOfMemoryException
继承
OutOfMemoryException
派生
属性

注解

OutOfMemoryException 使用具有值0x8007000E的 HRESULT COR_E_OUTOFMEMORY

有关 OutOfMemoryException实例的初始属性值列表,请参阅 OutOfMemoryException 构造函数。

注意

继承的 Data 属性的值始终 null

OutOfMemoryException 异常有两个主要原因:

  • 您试图将 StringBuilder 对象扩展为超出其 StringBuilder.MaxCapacity 属性定义的长度。

  • 公共语言运行时无法分配足够的连续内存来成功执行操作。 此异常可由需要内存分配的任何属性分配或方法调用引发。 有关 OutOfMemoryException 异常原因的详细信息,请参阅博客文章 “内存不足”不指物理内存

    这种类型的 OutOfMemoryException 异常表示灾难性故障。 如果选择处理异常,则应包括一个 catch 块,该块调用 Environment.FailFast 方法以终止应用并将条目添加到系统事件日志,如以下示例所示。

    using System;
    
    public class Example
    {
       public static void Main()
       {
          try {
             // Outer block to handle any unexpected exceptions.
             try {
                string s = "This";
                s = s.Insert(2, "is ");
    
                // Throw an OutOfMemoryException exception.
                throw new OutOfMemoryException();
             }
             catch (ArgumentException) {
                Console.WriteLine("ArgumentException in String.Insert");
             }
    
             // Execute program logic.
          }
          catch (OutOfMemoryException e) {
             Console.WriteLine("Terminating application unexpectedly...");
             Environment.FailFast(String.Format("Out of Memory: {0}",
                                                e.Message));
          }
       }
    }
    // The example displays the following output:
    //        Terminating application unexpectedly...
    
    open System
    
    try
        // Outer block to handle any unexpected exceptions.
        try
            let s = "This"
            let s = s.Insert(2, "is ")
    
            // Throw an OutOfMemoryException exception.
            raise (OutOfMemoryException())
        with
        | :? ArgumentException ->
            printfn "ArgumentException in String.Insert"
    
        // Execute program logic.
    with :? OutOfMemoryException as e ->
        printfn "Terminating application unexpectedly..."
        Environment.FailFast $"Out of Memory: {e.Message}"
    // The example displays the following output:
    //        Terminating application unexpectedly...
    
    Module Example
       Public Sub Main()
          Try
             ' Outer block to handle any unexpected exceptions.
             Try
                Dim s As String = "This"
                s = s.Insert(2, "is ")
    
                ' Throw an OutOfMemoryException exception.
                Throw New OutOfMemoryException()
             Catch e As ArgumentException
                Console.WriteLine("ArgumentException in String.Insert")
             End Try
             
             ' Execute program logic.
    
          Catch e As OutOfMemoryException
             Console.WriteLine("Terminating application unexpectedly...")
             Environment.FailFast(String.Format("Out of Memory: {0}",
                                                e.Message))
          End Try
       End Sub
    End Module
    ' The example displays the following output:
    '       Terminating application unexpectedly...
    

引发异常的一些条件以及可以采取的消除操作包括以下内容:

调用 StringBuilder.Insert 方法。

您尝试将 StringBuilder 对象的长度增加到超出其 StringBuilder.MaxCapacity 属性所指定的大小。 以下示例演示当示例尝试插入导致对象的 Length 属性超出其最大容量的字符串时,调用 StringBuilder.Insert(Int32, String, Int32) 方法引发的 OutOfMemoryException 异常。

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      StringBuilder sb = new StringBuilder(15, 15);
      sb.Append("Substring #1 ");
      try {
         sb.Insert(0, "Substring #2 ", 1);
      }
      catch (OutOfMemoryException e) {
         Console.WriteLine("Out of Memory: {0}", e.Message);
      }
   }
}
// The example displays the following output:
//    Out of Memory: Insufficient memory to continue the execution of the program.
open System
open System.Text

let sb = StringBuilder(15, 15)
sb.Append "Substring #1 "
|> ignore
try
    sb.Insert(0, "Substring #2 ", 1)
    |> ignore
with :? OutOfMemoryException as e ->
    printfn $"Out of Memory: {e.Message}"
// The example displays the following output:
//    Out of Memory: Insufficient memory to continue the execution of the program.
Imports System.Text

Module Example
   Public Sub Main()
      Dim sb As New StringBuilder(15, 15)
      sb.Append("Substring #1 ")
      Try
         sb.Insert(0, "Substring #2 ", 1)
      Catch e As OutOfMemoryException
         Console.WriteLine("Out of Memory: {0}", e.Message)
      End Try
   End Sub
End Module
' The example displays the following output:
'   Out of Memory: Insufficient memory to continue the execution of the program.

可以执行以下操作之一来解决此错误:

应用作为 32 位进程运行。

32 位进程可以在 32 位系统上最多分配 2GB 的虚拟用户模式内存,在 64 位系统上分配 4GB 的虚拟用户模式内存。 这样,公共语言运行时就更难在需要大型分配时分配足够的连续内存。 相比之下,64 位进程最多可以分配 8TB 的虚拟内存。 若要解决此异常,请将应用重新编译为面向 64 位平台。 有关面向 Visual Studio 中特定平台的信息,请参阅 如何:将项目配置为目标平台

应用 泄露非托管资源

尽管垃圾回收器能够释放分配给托管类型的内存,但它不管理分配给非托管资源的内存,例如操作系统句柄(包括文件句柄、内存映射文件、管道、注册表项和等待句柄)以及由 Windows API 调用或对内存分配函数(如 malloc)直接分配的内存块。 使用非托管资源的类型实现 IDisposable 接口。

如果使用使用非托管资源的类型,则应在使用完该类型后,请务必调用其 IDisposable.Dispose 方法。 (某些类型还实现在函数中与 Dispose 方法相同的 Close 方法。有关详细信息,请参阅 使用实现 IDisposable 主题的对象。

如果已创建使用非托管资源的类型,请确保已实现 Dispose 模式,并在必要时提供终结器。 有关详细信息,请参阅 实现 Dispose 方法Object.Finalize

尝试在 64 位进程中创建大型数组

默认情况下,.NET Framework 中的公共语言运行时不允许其大小超过 2GB 的单个对象。 若要替代此默认值,可以使用 <gcAllowVeryLargeObjects> 配置文件设置启用总大小超过 2 GB 的数组。 在 .NET Core 上,默认启用对大于 2 GB 的数组的支持。

你正在使用内存中非常大的数据集(例如数组、集合或数据库数据集)。

当驻留在内存中的数据结构或数据集变得如此之大时,公共语言运行时无法为其分配足够的连续内存时,OutOfMemoryException 异常结果。

若要防止 OutOfMemoryException 异常,必须修改应用程序,以便更少的数据驻留在内存中,或者数据划分为需要更小内存分配的段。 例如:

  • 如果要从数据库检索所有数据,然后在应用中筛选数据以最大程度地减少到服务器的行程,则应修改查询以仅返回应用所需的数据子集。 使用大型表时,多个查询几乎总是比检索单个表中的所有数据然后对其进行操作更有效。

  • 如果要执行用户动态创建的查询,则应确保查询返回的记录数受到限制。

  • 如果使用大型数组或其他集合对象,其大小会导致 OutOfMemoryException 异常,则应修改应用程序以处理子集中的数据,而不是一次性处理所有数据。

以下示例获取一个由 2 亿个浮点值组成的数组,然后计算其平均值。 该示例的输出显示,由于该示例在计算平均值之前将整个数组存储在内存中,因此将引发 OutOfMemoryException

using System;
using System.Collections.Generic;

public class Example
{
   public static void Main()
   {
      Double[] values = GetData();
      // Compute mean.
      Console.WriteLine("Sample mean: {0}, N = {1}",
                        GetMean(values), values.Length);
   }

   private static Double[] GetData()
   {
      Random rnd = new Random();
      List<Double> values = new List<Double>();
      for (int ctr = 1; ctr <= 200000000; ctr++) {
         values.Add(rnd.NextDouble());
         if (ctr % 10000000 == 0)
            Console.WriteLine("Retrieved {0:N0} items of data.",
                              ctr);
      }
      return values.ToArray();
   }

   private static Double GetMean(Double[] values)
   {
      Double sum = 0;
      foreach (var value in values)
         sum += value;

      return sum / values.Length;
   }
}
// The example displays output like the following:
//    Retrieved 10,000,000 items of data.
//    Retrieved 20,000,000 items of data.
//    Retrieved 30,000,000 items of data.
//    Retrieved 40,000,000 items of data.
//    Retrieved 50,000,000 items of data.
//    Retrieved 60,000,000 items of data.
//    Retrieved 70,000,000 items of data.
//    Retrieved 80,000,000 items of data.
//    Retrieved 90,000,000 items of data.
//    Retrieved 100,000,000 items of data.
//    Retrieved 110,000,000 items of data.
//    Retrieved 120,000,000 items of data.
//    Retrieved 130,000,000 items of data.
//
//    Unhandled Exception: OutOfMemoryException.
open System

let getData () =
    let rnd = Random()
    [|  for i = 1 to 200000000 do
            rnd.NextDouble()
            if i % 10000000 = 0 then
                printfn $"Retrieved {i:N0} items of data." |]
    
let getMean values =
    let sum = Array.sum values

    sum / float values.Length

let values = getData ()
// Compute mean.
printfn $"Sample mean: {getMean values}, N = {values.Length}"

// The example displays output like the following:
//    Retrieved 10,000,000 items of data.
//    Retrieved 20,000,000 items of data.
//    Retrieved 30,000,000 items of data.
//    Retrieved 40,000,000 items of data.
//    Retrieved 50,000,000 items of data.
//    Retrieved 60,000,000 items of data.
//    Retrieved 70,000,000 items of data.
//    Retrieved 80,000,000 items of data.
//    Retrieved 90,000,000 items of data.
//    Retrieved 100,000,000 items of data.
//    Retrieved 110,000,000 items of data.
//    Retrieved 120,000,000 items of data.
//    Retrieved 130,000,000 items of data.
//
//    Unhandled Exception: OutOfMemoryException.
Imports System.Collections.Generic

Module Example
   Public Sub Main()
      Dim values() As Double = GetData()
      ' Compute mean.
      Console.WriteLine("Sample mean: {0}, N = {1}",
                        GetMean(values), values.Length)
   End Sub
   
   Private Function GetData() As Double()
      Dim rnd As New Random()
      Dim values As New List(Of Double)()
      For ctr As Integer = 1 To 200000000
         values.Add(rnd.NextDouble)
         If ctr Mod 10000000 = 0 Then
            Console.WriteLine("Retrieved {0:N0} items of data.",
                              ctr)
         End If
      Next
      Return values.ToArray()
   End Function
   
   Private Function GetMean(values() As Double) As Double
      Dim sum As Double = 0
      For Each value In values
         sum += value
      Next
      Return sum / values.Length
   End Function
End Module
' The example displays output like the following:
'    Retrieved 10,000,000 items of data.
'    Retrieved 20,000,000 items of data.
'    Retrieved 30,000,000 items of data.
'    Retrieved 40,000,000 items of data.
'    Retrieved 50,000,000 items of data.
'    Retrieved 60,000,000 items of data.
'    Retrieved 70,000,000 items of data.
'    Retrieved 80,000,000 items of data.
'    Retrieved 90,000,000 items of data.
'    Retrieved 100,000,000 items of data.
'    Retrieved 110,000,000 items of data.
'    Retrieved 120,000,000 items of data.
'    Retrieved 130,000,000 items of data.
'
'    Unhandled Exception: OutOfMemoryException.

以下示例通过处理传入数据而不将整个数据集存储在内存中来消除 OutOfMemoryException 异常,如有必要,将数据序列化到文件以允许进一步处理(这些行在示例中注释掉,因为在这种情况下,它们生成大小大于 1GB 的文件),并将计算平均值和事例数返回到调用例程。

using System;
using System.IO;

public class Example
{
   public static void Main()
   {
      Tuple<Double, long> result = GetResult();
      Console.WriteLine("Sample mean: {0}, N = {1:N0}",
                        result.Item1, result.Item2);
   }

   private static Tuple<Double, long> GetResult()
   {
      int chunkSize = 50000000;
      int nToGet = 200000000;
      Random rnd = new Random();
      // FileStream fs = new FileStream(@".\data.bin", FileMode.Create);
      // BinaryWriter bin = new BinaryWriter(fs);
      // bin.Write((int)0);
      int n = 0;
      Double sum = 0;
      for (int outer = 0;
           outer <= ((int) Math.Ceiling(nToGet * 1.0 / chunkSize) - 1);
           outer++) {
         for (int inner = 0;
              inner <= Math.Min(nToGet - n - 1, chunkSize - 1);
              inner++) {
            Double value = rnd.NextDouble();
            sum += value;
            n++;
            // bin.Write(value);
         }
      }
      // bin.Seek(0, SeekOrigin.Begin);
      // bin.Write(n);
      // bin.Close();
      return new Tuple<Double, long>(sum/n, n);
   }
}
// The example displays output like the following:
//    Sample mean: 0.500022771458399, N = 200,000,000
open System
// open System.IO

let getResult () =
    let chunkSize = 50000000
    let nToGet = 200000000
    let rnd = Random()
    // use fs = new FileStream(@".\data.bin", FileMode.Create)
    // use bin = new BinaryWriter(fs)
    // bin.Write 0
    let mutable n = 0
    let mutable sum = 0.
    for _ = 0 to int (ceil (nToGet / chunkSize |> float) - 1.) do
        for _ = 0 to min (nToGet - n - 1) (chunkSize - 1) do
            let value = rnd.NextDouble()
            sum <- sum + value
            n <- n + 1
            // bin.Write(value)
    // bin.Seek(0, SeekOrigin.Begin) |> ignore
    // bin.Write n
    sum / float n, n

let mean, n = getResult ()
printfn $"Sample mean: {mean}, N = {n:N0}"

// The example displays output like the following:
//    Sample mean: 0.500022771458399, N = 200,000,000
Imports System.IO

Module Example
   Public Sub Main()
      Dim result As Tuple(Of Double, Long) = GetResult()
      Console.WriteLine("Sample mean: {0}, N = {1:N0}",
                        result.Item1, result.Item2)
   End Sub

   Private Function GetResult As Tuple(Of Double, Long)
      Dim chunkSize As Integer = 50000000
      Dim nToGet As Integer = 200000000
      Dim rnd As New Random()
'       Dim fs As New FileStream(".\data.bin", FileMode.Create)
'       Dim bin As New BinaryWriter(fs)
'       bin.Write(CInt(0))
      Dim n As Integer
      Dim sum As Double
      For outer As Integer = 0 To CInt(Math.Ceiling(nToGet/chunkSize) - 1)
         For inner = 0 To Math.Min(nToGet - n - 1, chunkSize - 1)
            Dim value As Double = rnd.NextDouble()
            sum += value
            n += 1
'            bin.Write(value)
         Next
      Next
'       bin.Seek(0, SeekOrigin.Begin)
'       bin.Write(n)
'       bin.Close()
      Return New Tuple(Of Double, Long)(sum/n, n)
   End Function
End Module
' The example displays output like the following:
'   Sample mean: 0.500022771458399, N = 200,000,000

重复连接大型字符串。

由于字符串是不可变的,因此每个字符串串联操作都会创建新的字符串。 对于小字符串或少量串联操作的影响是微不足道的。 但是,对于大型字符串或大量串联操作,字符串串联可能会导致大量内存分配和内存碎片、性能不佳,并可能 OutOfMemoryException 异常。

连接大型字符串或执行大量串联操作时,应使用 StringBuilder 类而不是 String 类。 操作完字符串后,通过调用 StringBuilder.ToString 方法将 StringBuilder 实例转换为字符串。

在内存中固定大量对象。

长时间固定内存中的大量对象会使垃圾回收器难以分配连续的内存块。 如果已在内存中固定了大量对象,例如,通过在 C# 中使用 fixed 语句或调用具有句柄类型 GCHandleType.PinnedGCHandle.Alloc(Object, GCHandleType) 方法,则可以执行以下操作来解决 OutOfMemoryException 异常。

以下Microsoft中间(MSIL)指令引发 OutOfMemoryException 异常:

构造函数

OutOfMemoryException()

初始化 OutOfMemoryException 类的新实例。

OutOfMemoryException(SerializationInfo, StreamingContext)
已过时.

使用序列化的数据初始化 OutOfMemoryException 类的新实例。

OutOfMemoryException(String, Exception)

使用指定的错误消息和对作为此异常原因的内部异常的引用初始化 OutOfMemoryException 类的新实例。

OutOfMemoryException(String)

使用指定的错误消息初始化 OutOfMemoryException 类的新实例。

属性

Data

获取键/值对的集合,这些键/值对提供有关异常的其他用户定义的信息。

(继承自 Exception)
HelpLink

获取或设置与此异常关联的帮助文件的链接。

(继承自 Exception)
HResult

获取或设置 HRESULT,它是分配给特定异常的编码数值。

(继承自 Exception)
InnerException

获取导致当前异常的 Exception 实例。

(继承自 Exception)
Message

获取描述当前异常的消息。

(继承自 Exception)
Source

获取或设置导致错误的应用程序或对象的名称。

(继承自 Exception)
StackTrace

获取调用堆栈上即时帧的字符串表示形式。

(继承自 Exception)
TargetSite

获取引发当前异常的方法。

(继承自 Exception)

方法

Equals(Object)

确定指定的对象是否等于当前对象。

(继承自 Object)
GetBaseException()

在派生类中重写时,返回一个或多个后续异常的根本原因 Exception

(继承自 Exception)
GetHashCode()

用作默认哈希函数。

(继承自 Object)
GetObjectData(SerializationInfo, StreamingContext)
已过时.

在派生类中重写时,使用有关异常的信息设置 SerializationInfo

(继承自 Exception)
GetType()

获取当前实例的运行时类型。

(继承自 Exception)
MemberwiseClone()

创建当前 Object的浅表副本。

(继承自 Object)
ToString()

创建并返回当前异常的字符串表示形式。

(继承自 Exception)

事件

SerializeObjectState
已过时.

序列化异常以创建包含有关异常的序列化数据的异常状态对象时发生。

(继承自 Exception)

适用于

另请参阅