microsoftproject2013 Project 内存不足,无法完成操作.要释放出可用的内存,请关闭不使用程序,项目或窗口,再试一次

一提到托管代码中出现内存泄漏,很多开发人员的第一反应都认为这是不可能的。毕竟垃圾收集器 (GC) 会负责管理所有的内存,没错吧?但要知道,垃圾收集器只处理托管内存。基于 Microsoft(R) .NET Framework 的应用程序中大量使用了非托管内存,这些非托管内存既可以被公共语言运行库 (CLR) 使用,也可以在与非托管代码进行互操作时被程序员显式使用。在某些情况下,垃圾管理器似乎在逃避自己的职责,没有对托管内存进行有效处理。这通常是由于不易察觉的(也可能是非常明显的)编程错误妨碍了垃圾收集器的正常工作而造成的。作为经常与内存打交道的程序员,我们仍需要检查自己的应用程序,确保它们不会发生内存泄漏并能够合理有效地使用所需内存。
.NET 应用程序中的内存
您大概已经知道,.NET 应用程序中要使用多种类型的内存,包括:堆栈、非托管堆和托管堆。这里我们需要简单回顾一下。
堆栈用于存储应用程序执行过程中的局部变量、方法参数、返回值和其他临时值。堆栈按照每个线程进行分配,并作为每个线程完成其工作的一个暂存区。垃圾收集器并不负责清理堆栈,因为为方法调用预留的堆栈会在方法返回时被自动清理。但是请注意,垃圾收集器知道在堆栈上存储的对象的引用。当对象在一种方法中被实例化时,该对象的引用(32 位或 64 位整型值,取决于平台类型)将保留在堆栈中,而对象自身却存储于托管堆中,并在变量超出范围时被垃圾收集器收集。
非托管堆用于运行时数据结构、方法表、Microsoft 中间语言 (MSIL)、JITed 代码等。非托管代码根据对象的实例化方式将其分配在非托管堆或堆栈上。托管代码可通过调用非托管的 Win32(R) API 或实例化 COM 对象来直接分配非托管堆内存。CLR 出于自身的数据结构和代码原因广泛地使用非托管堆。
托管堆是用于分配托管对象的区域,同时也是垃圾收集器的域。CLR 使用分代压缩垃圾收集器。垃圾收集器之所以称为分代式,是由于它将垃圾收集后保留下来的对象按生存时间进行划分,这样做有助于提高性能。所有版本的 .NET Framework 都采用三代分代方法:第 0 代、第 1 代和第 2 代(从年轻代到年老代)。垃圾收集器之所以称为压缩式,是因为它将对象重新定位于托管堆上,从而能够消除漏洞并保持可用内存的连续性。移动大型对象的开销很高,因此垃圾收集器将这些大型对象分配在独立的且不会压缩的大型对象堆上。有关托管堆和垃圾收集器的详细信息,请参阅 Jeffrey Richter 所著的分为两部分的系列文章“”和“”。虽然该文的写作是基于 .NET Framework 1.0,而且 .NET 垃圾收集器已经有所改进,但是其中的核心思想与 1.1 版或 2.0 版是保持一致的。
很多迹象能够表明应用程序正在发生内存泄漏。或许应用程序正在引发 OutOfMemoryException。或许应用程序因启动了虚拟内存与硬盘的交换而变得响应迟缓。或许出现任务管理器中内存的使用率逐渐(也可能突然地)上升。当怀疑应用程序发生内存泄漏时,必须首先确定是哪种类型的内存发生泄漏,以便您将调试工作的重点放在合适的区域。使用 PerfMon 来检查用于应用程序的下列性能计数器:Process/Private Bytes、.NET CLR Memory/# Bytes in All Heaps 和 .NET CLR LocksAndThreads/# of current logical Threads。Process/Private Bytes 计数器用于报告系统中专门为某一进程分配而无法与其他进程共享的所有内存。.NET CLR Memory/# Bytes in All Heaps 计数器报告第 0 代、第 1 代、第 2 代和大型对象堆的合计大小。.NET CLR LocksAndThreads/# of current logical Threads 计数器报告 AppDomain 中逻辑线程的数量。如果应用程序的逻辑线程计数出现意想不到的增大,则表明线程堆栈发生泄漏。如果 Private Bytes 增大,而 # Bytes in All Heaps 保持不变,则表明非托管内存发生泄漏。如果上述两个计数器均有所增加,则表明托管堆中的内存消耗在增长。
堆栈内存泄漏
虽然有可能出现堆栈空间不足而导致在受托管的情况下引发 StackOverflowException 异常,但是方法调用期间使用的任何堆栈空间都会在该方法返回后被回收。因此,实际上只有在两种情况下才会发生堆栈空间泄漏。一种情况是进行一种极其耗费堆栈资源并且从不返回的方法调用,从而使关联的堆栈帧无法得到释放。另一种情况是发生线程泄漏,从而使线程的整个堆栈发生泄漏。如果应用程序为了执行后台工作而创建了工作线程,但却忽略了正常终止这些进程,则可引起线程泄漏。默认情况下,最新桌面机和服务器版的 Windows(R) 堆栈大小均为 1MB。因此如果应用程序的 Process/Private Bytes 定期增大 1MB,同时 .NET CLR LocksAndThreads/# of current logical Threads 也相应增大,那么罪魁祸首很可能是线程堆栈泄漏。图 1 显示了(恶意的)多线程逻辑导致的不正确的线程清理示例。
&Figure 1 清理错误线程
using System.T
namespace MsdnMag.ThreadForker {
class Program {
static void Main() {
while(true) {
Console.WriteLine(
"Press &ENTER& to fork another thread...");
Console.ReadLine();
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
static void ThreadProc() {
Console.WriteLine("Thread #{0} started...",
Thread.CurrentThread.ManagedThreadId);
// Block until current thread terminates - i.e. wait forever
Thread.CurrentThread.Join();
当一个线程启动后会显示其线程 ID,然后尝试自联接。联接会导致调用线程停止等待另一线程的终止。这样该线程就会陷入一个类似于先有鸡还是先有蛋的尴尬局面之中 — 线程要等待自身的终止。在任务管理器下查看该程序,会发现每次按 &Enter& 时,其内存使用率会增长 1MB(即线程堆栈的大小)。
每次经过循环时,Thread 对象的引用都会被删除,但垃圾收集器并未回收分配给线程堆栈的内存。托管线程的生存期并不依赖于创建它的 Thread 对象。如果您只是因为丢失了所有与 Thread 对象相关联的引用而不希望垃圾收集器将一个仍在运行的进程终止,这种不依赖性是非常有好处的。由此可见,垃圾收集器只是收集 Thread 对象,而非实际托管的线程。只有在其 ThreadProc 返回后或者自身被直接终止的情况下,托管线程才会退出(其线程堆栈的内存不会释放)。因此,如果托管线程的终止方式不正确,分配至其线程堆栈的内存就会发生泄漏。
非托管堆内存泄漏
如果总的内存使用率增加,而逻辑线程计数和托管堆内存并未增加,则表明非托管堆出现内存泄漏。我们将对导致非托管堆中出现内存泄漏的一些常见原因进行分析,其中包括与非托管代码进行互操作、终结器被终止以及程序集泄漏。
与非托管代码进行互操作:这是内存泄漏的起因之一,涉及到与非托管代码的互操作,例如在 COM Interop 中通过 P/Invoke 和 COM 对象使用 C 样式的 DLL。垃圾收集器无法识别非托管内存,而正是在托管代码的编写过程中错误地使用了非托管内存,才导致内存出现泄漏。如果应用程序与非托管代码进行互操作,要逐步查看代码并检查非托管调用前后内存的使用情况,以验证内存是否被正确回收。如果内存未被正确回收,则使用传统的调试方法在非托管组件中查找泄漏。
终结器被终止:当一个对象的终结器未被调用,并且其中含有用于清理对象所分配的非托管内存的代码时,会造成隐性泄漏。在正常情况下,终结器都将被调用,但是 CLR 不会对此提供任何保证。虽然未来可能会有所变化,但是目前的 CLR 版本仅使用一个终结器线程。请考虑这样一种情况,运行不正常的终结器试图将信息记录到脱机的数据库。如果该运行不正常的终结器反复尝试对数据库进行错误的访问而从不返回,则“运行正常”的终结器将永远没有机会运行。该问题会不时出现,因为这取决于终结器在终结队列中的位置以及其他终结器采取何种行为。
当 AppDomain 拆开时,CLR 将通过运行所有终结器来尝试清理终结器队列。被延迟的终结器可阻止 CLR 完成 AppDomain 拆开。为此,CLR 在该进程上做了超时操作,随后将停止该终止进程。但是这并不意味着世界末日已经来临。因为通常情况下,大多数应用程序只有一个 AppDomain,而只有进程被关闭才会导致 AppDomain 的拆开。当操作系统进程被关闭,操作系统会对该进程资源进行恢复。但不幸的是,在诸如 ASP.NET 或 SQL Server(TM) 之类的宿主情况下,AppDomain 的拆开并不意味着宿主进程的结束。另一个 AppDomain 会在同一进程中启动。任何因自身终结器未运行而被组件泄漏的非托管内存都将继续保持未引用状态,无法被访问,并且占用一定空间。因为内存的泄漏会随着时间的推移越来越严重,所以这将带来灾难性的后果。
在 .NET 1.x 中,唯一的解决方法是结束并重新启动该进程。.NET Framework 2.0 中引入了关键的终结器,指明在 AppDomain 关闭期间,终结器将清理非托管资源并必须获得运行的机会。有关详细信息,请参阅 Stephen Toub 的文章:“”。
程序集泄漏:程序集泄漏相对来说要常见一些。一旦程序集被加载,它只有在 AppDomain 被卸载的情况下才能被卸载。程序集泄漏也正是由此引发的。大多数情况下,除非程序集是被动态生成并加载的,否则这根本不算个问题。下面我们就来看一看动态代码生成造成的泄漏,特别要详细分析 XmlSerializer 的泄漏。
动态代码生成有时会泄漏我们需要动态生成代码。也许应用程序具有与 Microsoft Office 相似的宏脚本编写接口来提高其扩展性。也许某个债券定价引擎需要动态加载定价规则,以便最终用户能够创建自己的债券类型。也许应用程序是用于 Python 的动态语言运行库/编译器。在很多情况下,出于性能方面的考虑,最好是通过编写宏、定价规则或 MSLI 代码来解决问题。您可以使用 System.CodeDom 来动态生成 MSLI。
图 2 中的代码可在内存中动态生成一个程序集。该程序集可被重复调用而不会出现问题。遗憾的是,一旦宏、定价规则或代码有所改变,就必须重新生成新的动态程序集。原有的程序集将不再使用,但是却无法从内存中清除,加载有程序集的 AppDomain 也无法被卸载。其代码、JITed 方法和其他运行时数据结构所用的非托管堆内存已经被泄漏。(托管内存也在动态生成的类上以任意静态字段的形式被泄漏。)要检测到这一问题,我们尚无良方妙计。如果您正使用 System.CodeDom 动态地生成 MSLI,请检查是否重新生成了代码。如果有代码生成,那么您的非托管堆内存正在发生泄漏。
&Figure 2 在内存中动态生成程序集
CodeCompileUnit program = new CodeCompileUnit();
CodeNamespace ns = new
CodeNamespace("MsdnMag.MemoryLeaks.CodeGen.CodeDomGenerated");
ns.Imports.Add(new CodeNamespaceImport("System"));
program.Namespaces.Add(ns);
CodeTypeDeclaration class1 = new CodeTypeDeclaration("CodeDomHello");
ns.Types.Add(class1);
CodeEntryPointMethod start = new CodeEntryPointMethod();
start.ReturnType = new CodeTypeReference(typeof(void));
CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("System.Console"), "WriteLine",
new CodePrimitiveExpression("Hello, World!"));
start.Statements.Add(cs1);
class1.Members.Add(start);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = pileAssemblyFromDom(
new CompilerParameters(), program);
目前有两种主要方法可解决这一问题。第一种方法是将动态生成的 MSLI 加载到子 AppDomain 中。子 AppDomain 能够在所生成的代码发生改变时被卸载,并运行一个新的子 AppDomain 来托管更新后的 MSLI。这种方法在所有版本的 .NET Framework 中都是行之有效的。
.NET Framework 2.0 中还引入了另外一种叫做轻量级代码生成的方法,也称动态方法。使用 DynamicMethod 可以显式发出 MSLI 的操作码来定义方法体,然后可以直接通过 DynamicMethod.Invoke 或通过合适的委托来调用 DynamicMethod。
DynamicMethod dm = new DynamicMethod("tempMethod" +
Guid.NewGuid().ToString(), null, null, this.GetType());
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Hello, World!");
MethodInfo cw = typeof(Console).GetMethod("WriteLine",
new Type[] { typeof(string) });
il.Emit(OpCodes.Call, cw);
dm.Invoke(null, null);
动态方法的主要优势是 MSLI 和所有相关代码生成数据结构均被分配在托管堆上。这意味着一旦 DynamicMethod 的最后一个引用超出范围,垃圾收集器就能够回收内存。
XmlSerializer 泄漏:.NET Framework 中的某些部分(例如 XmlSerializer)会在内部使用动态代码生成。请看下列典型的 XmlSerializer 代码:
XmlSerializer serializer = new XmlSerializer(typeof(Person));
serializer.Serialize(outputStream, person);
XmlSerializer 构造函数将使用反射来分析 Person 类,并藉此生成一对由 XmlSerializationReader 和 XmlSerializationWriter 派生而来的类。它将创建临时的 C# 文件,将结果文件编译成临时程序集,并最终将该程序集加载到进程。通过这种方式生成的代码同样需要相当大的开销。因此 XmlSerializer 对每种类型的临时程序集进行缓存。也就是说,下一次为 Person 类创建 XmlSerializer 时,会使用缓存的程序集,而不再生成新的程序集。
默认情况下,XmlSerializer 所使用的 XmlElement 名称就是该类的名称。因此,Person 将被序列化为:
&?xml version="1.0" encoding="utf-8"?&
&Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"&
&Id&5d49c002-089d-4445-ac4a-acb&/Id&
&FirstName&John&/FirstName&
&LastName&Doe&/LastName&
有时有必要在不改变类名称的前提下改变根元素的名称。(要与现有架构兼容可能需要根元素名称。)因此 Person 可能需要被序列化为 &PersonInstance&。XmlSerializer 构造函数能够很方便地被重载,将根元素名称作为第二参数,如下所示:
XmlSerializer serializer = new XmlSerializer(typeof(Person),
new XmlRootAttribute("PersonInstance"));
当应用程序开始对 Person 对象进行序列化/反序列化时,一切运转正常,直至引发 OutOfMemoryException。对 XmlSerializer 构造函数的重载并不会对动态生成的程序集进行缓存,而是在每次实例化新的 XmlSerializer 时生成新的临时程序集。这时应用程序以临时程序集的形式泄漏非托管内存。
要修复该泄漏,请在类中使用 XmlRootAttribute 以更改序列化类型的根元素名称:
[XmlRoot("PersonInstance")]
public class Person {
如果直接将属性赋予类型,则 XmlSerializer 对为类型所生成的程序集进行缓存,从而避免了内存的泄漏。如果需要对根元素名称进行动态切换,应用程序能够利用工厂对其进行检索,从而对 XmlSerializer 实例自身进行缓存。
XmlSerializer serializer = XmlSerializerFactory.Create(
typeof(Person), "PersonInstance");
XmlSerializerFactory 是我创建的一个类,它可以使用 PersonInstance 根元素名称来检查 Dictionary&Tkey, Tvalue& 中是否包含有用于 Person 的 Xmlserializer。如果包含,则返回该实例。如果不包含,则创建一个新的实例,并将其存储在哈希表中返回给调用方。
“泄漏”托管堆内存
现在让我们关注一下托管内存的“泄漏”。在处理托管内存时,垃圾收集器会帮助我们完成绝大部分的工作。我们需要向垃圾收集器提供工作所需的信息。但是,在很多场合下,垃圾收集器无法有效地工作,导致需要使用比正常工作要求更高的托管内存。这些情况包括大型对象堆碎片、不必要的根引用以及中年危机。
大型对象堆碎片
如果一个对象的大小为 85,000 字节或者更大,就要被分配在大型对象堆上。请注意,这里是指对象自身的大小,并非任何子对象的大小。以下列类为例:
public class Foo {
private byte[] m_buffer = new byte[90000]; // large object heap
由于 Foo 实例仅含有一个 4 字节(32 位框架)或 8 字节(64 位框架)的缓冲区引用,以及一些 .NET Framework 使用的内务数据,因此将被分配在普通的分代式托管堆上。缓冲区将分配在大型对象堆上。
与其他的托管堆不同,由于移动大型对象耗费资源,所以大型对象堆不会被压缩。因此,当大型对象被分配、释放并清理后,就会出现空隙。根据使用模式的不同,大型对象堆中的这些空隙可能会使内存使用率明显高于当前分配的大型对象所需的内存使用率。本月下载中包含的 LOHFragmentation 应用程序会在大型对象堆中随机分配和释放字节数组,从而用实例证实了这一点。应用程序运行几次后,能通过释放字节数组的方式创建出恰好与空隙相符的新的字节数组。在应用程序的另外几次运行中,则未出现这种情况,内存需要量远远大于当前分配的字节数组的内存需要量。您可以使用诸如 CLRProfiler 的内存分析器来将大型对象堆的碎片可视化。图 3 中的红色区域为已分配的字节数组,而白色区域则代表未分配的空间。
图 3 CLRProfiler 中的大型对象堆 (单击该图像获得较大视图)
目前尚无一种单一的解决方案能够避免大型对象堆碎片的产生。您可以使用类似 CLRProfiler 的工具对应用程序的内存使用情况,特别是大型对象堆中的对象类型进行检查。如果碎片是由于重新分配缓冲区而产生的,则请保持固定数量的重用缓冲区。如果碎片是由于大量字符串串连而产生的,请检查 System.Text.StringBuilder 类是否能够减少创建临时字符串的数量。基本策略是要确定如何降低应用程序对临时大型对象的依赖,而临时大型对象正是大型对象堆中产生空隙的原因所在。
不必要的根引用
让我们思考一下垃圾收集器是如何决定回收内存的时间。当 CLR 试图分配内存并保留不足的内存时,它就在扮演着垃圾收集器的角色。垃圾收集器列出了所有的根引用,包括位于任何线程的调用堆栈上的静态字段和域内局部变量。垃圾收集器将这些引用标记为可访问,并跟据这些对象所包含的引用,将其同样标记为可访问。这一过程将持续进行,直至所有可访问的引用均被访问。任何没有被标记的对象都是无法访问的,因此是垃圾。垃圾收集器对托管堆进行压缩,整理引用以指向它们在堆中的新位置,并将控件返回给 CLR。如果释放充足的内存,则使用此释放的内存进行分配。如果释放的内存不足,则向操作系统请求额外的内存。
如果我们忘记清空根引用,系统会立即阻止垃圾收集器有效地释放内存,从而导致应用程序需要更多的内存。问题可能微妙,例如一种方法,它能够在做出与查询数据库或调用某个 Web 服务相类似的远程调用前为临时对象创建大型图形。如果垃圾收集发生在远程调用期间,则整个图形被标记为可访问的,并不会收集。这样会导致更大的开销,因为在收集中得以保留的对象将被提升到下一代,这将引起所谓的中年危机。
中年危机不会使应用程序去购买一辆保时捷。但它却可以造成托管堆内存的过度使用,并使垃圾收集器花费过多的处理器时间。正如前面所提到的,垃圾收集器使用分代式算法,采取试探性的推断,它会认为如果对象已经存活一段时期,则有可能存活更长的一段时期。例如,在 Windows 窗体应用程序中,应用程序启动时会创建主窗体,主窗体关闭时应用程序则退出。对于垃圾收集器来说,持续地验证主窗体是否正在被引用是一件浪费资源的事。当系统需要内存以满足分配请求时,会首先执行第 0 代收集。如果没有足够的可用内存,则执行第 1 代收集。如果仍然无法满足分配请求,则继续执行第 2 代收集,这将导致整个托管堆以极大的开销进行清理工作。第 0 代收集的开销相对较低,因为只有当前被分配的对象才被认为是需要收集的。
如果对象有继续存活至第 1 代(或更严重至第 2 代)的趋势,但却随即死亡,此时就会出现中年危机。这样做的效果是使得开销低的第 0 代收集转变为开销大得多的第 1 代(或第 2 代)收集。为什么会发生这种现象呢?请看下面的代码:
class Foo {
~Foo() { }
对象将始终在第 1 代收集中被回收!终结器 ~Foo() 使我们可以实现对象的代码清理,除非强行终止 AppDomain,否则代码将在对象内存被释放前运行。垃圾收集器的任务是尽快地释放尽可能多的托管内存。终结器是由用户编写的代码,并且毫无疑问可以执行任何操作。虽然我们并不建议,但是终结器也会执行一些愚蠢的操作,例如将日志记录到数据库或调用 Thread.Sleep(int.MaxValue)。因此,当垃圾收集器发现具有终结器但未被引用的对象时,会将该对象加入到终结队列中,并继续工作。该对象由此在垃圾收集中得以保留,被提升一代。这里甚至为其准备了一个性能计数器:.NET CLR Memory-Finalization Survivors,可显示最后一次垃圾收集期间由于具有终结器而得以保留的对象的数量。最后,终结器线程将运行对象的终结器,随后对象即被收集。但此时您已经从开销低的第 0 代收集转变为第 1 代收集,而您仅仅是添加了一个终结器!
大多数情况下,编写托管代码时终结器并不是必不可少的。只有当托管对象具有需要清理的非托管资源的引用时,才需要终结器。而且即使这样,您也应该使用 SafeHandle 派生类型来对非托管资源进行包装,而不要使用终结器。此外,如果您使用非托管资源或其他实现 Idispoable 的托管类型,请实现 Dispose 模式来让使用对象的用户大胆地清理资源,并避免使用任何相关的终结器。
如果一个对象仅拥有其他托管对象的引用,垃圾收集器将对未引用的对象进行清理。这一点与 C++ 截然不同,在 C++ 中必须在子对象上调用删除命令。如果终结器为空或仅仅将子对象引用清空,请将其删除。将对象不必要地提升至更高一代将对性能造成影响,使清理开销更高。
还有一些做法会导致中年危机,例如在进行查询数据库、在另一线程上阻塞或调用 Web 服务等阻塞调用之前保持对对象的持有。在调用过程中,可以发生一次或多次收集,并由此使得开销低的第 0 代对象提升至更高一代,从而再次导致更高的内存使用率和收集成本。
还有一种情况,它与事件处理程序和回调一起发生并且更难理解。我将以 ASP.NET 为例,但同样类型的问题也会发生在任何应用程序中。考虑一下执行一次开销很大的查询,然后等上 5 分钟才可以缓存查询结果的情况。查询是属于页面查询,并基于查询字符串参数来进行。当一项内容从缓存中删除时,事件处理程序将进行记录,以监视缓存行为。(参见图 4)。
&Figure 4 记录从缓存中移除的项
protected void Page_Load(object sender, EventArgs e) {
string cacheKey = buildCacheKey(Request.Url, Request.QueryString);
object cachedObject = Cache.Get(cacheKey);
if(cachedObject == null) {
cachedObject = someExpensiveQuery();
Cache.Add(cacheKey, cachedObject, null,
Cache.NoAbsoluteExpiration,
TimeSpan.FromMinutes(5), CacheItemPriority.Default,
new CacheItemRemovedCallback(OnCacheItemRemoved));
... // Continue with normal page processing
private void OnCacheItemRemoved(string key, object value,
CacheItemRemovedReason reason) {
... // Do some logging here
看上去正常的代码实际上隐含着严重的错误。所有这些 ASP.NET Page 实例都变成了“永世长存”的对象。OnCacheItemRemoved 是一个实例方法,CacheItemRemovedCallback 委托中包含了一个隐式的“this”指针,这里的“this”即为 Page 实例。该委托被添加至 Cache 对象。这样,就会产生一个从 Cache 到委托再到 Page 实例的依赖关系。在进行垃圾收集时,可以一直从根引用(Cache 对象)访问 Page 实例。这时,Page 实例(以及在呈现时它所创建的所有临时对象)至少需要等待五分钟才能被收集,在此期间,它们都有可能被提升至第 2 代。幸运地是,有一种简单的方法能够解决该示例中的问题。请将回调函数变为静态。Page 实例上的依赖关系就会被打破,从而可以像第 0 代对象一样以很低的开销来进行收集。
我已经就 .NET 应用程序中能够导致内存泄漏或内存消耗过度的各种问题进行了讨论。虽然 .NET 可减少您对内存方面的关注程度,但是您仍必须关注应用程序的内存使用情况,以确保应用程序高效正常运行。虽然应用程序被托管,但这并不意味着您可以依靠垃圾收集器就能解决所有问题而将良好的软件工程实践束之高阁。虽然在应用程序的开发和测试阶段,您必须对其内存性能进行持续不断的监视。但是这样做非常值得。要记住,只有让用户满意才称得上是功能良好的应用程序。
James Kovacs居住在加拿大阿尔伯达省卡尔加里市,同时身为独立架构师、开发人员和培训师,对各个领域都很精通,尤其擅长 .NET Framework、安全和企业应用程序开发。James Kovacs 是 Microsoft 的解决方案架构 MVP,拥有哈佛大学硕士学位。James 的联系方式如下: 或 。
Current IssueReceive the MSDN Flash e-mail newsletter every other week, with news and information personalized to your interests and areas of focus.0x7c9318c5指令引用的0x内存,该内存不能为read.要终止程序,请单击确定_百度知道
0x7c9318c5指令引用的0x内存,该内存不能为read.要终止程序,请单击确定
0x7c9318c5指令引用的0x内存,该内存不能为read.要终止程序,请单击确定
必要时重装系统。 【4】打开或关闭IE;Microsoft&#92,回车,(一般来解决办法就是卸载重新安装这个问题的出现比较普遍,主要有几个方面,然后在命令提示符后击鼠标右键,打“粘贴”,对电脑全盘杀毒,直到屏幕滚动停止为止。 (2)运行regedit进入注册表。【2】硬件上的原因;SOFTWARE&#92,主要是内存条不兼容引起的.exe &#47,问题才能解决),及时安装官方发行的补丁。【3】系统或其它软件引起的,把不必要运行的软件都去掉),卸载有问题的软件,必要时更换内存、游戏等出现该内存不能为read或written、升级或更换其它版本,可以复制这条指令, 在HKEY_LOCAL_MACHINE&#92,软件冲突;ShellExecuteHooks 下。 ★下面有两种处理方法可以试试、QQ;*;s %1 怕输入错误,(用360等检查开机运行的软件.dll) do regsvr32;CurrentVersion&#92,首先想到可能就是这款软件的问题。 (2)某个软件出现的问题,在命令提示符下输入下面命令 for %1 in (%windir%\Explorer&#92,不行只有卸载该软件,这里主要是看看开机时运行的软件。(1)系统本身有问题:【如果不行只有恢复或重装系统了】(1)试用命令排除 开始-运行- 输入cmd-- 回车, 将其他的删除;system32&#92,耐心等待,应该只有一个正常的键值{AEB-11d0-97EE-00C04FD91972};Windows&#92:【1】病毒引起的
其他类似问题
其他1条回答
8 0x0008 储存体空间不足?,会被IE5。 124 0x007c 系统呼叫层次不正确,而且是不同品牌的内存条混插或者买了二手内存时。 111 0x006f 档名太长:打开IE浏览器或者没过几分钟就会出现&quot。 180 0x00b4 系统发现不正确的区段号码,出现故障的原因有好多种。该内存不能为“read”。 161 0x00a1 指定的路径不正确。 15 0x000f 系统找不到指定的磁碟机:&#92,即内存方面有问题,其访问权已经不属于该应用程序,函数就会将所新开辟的内存区地址返回给应用程序。 201 0x00c9 作业系统无法执行 %1。 193 0x00c1 %1 不是正确的 win32 应用程式。 206 0x00ce 档案名称或副档名太长,在每一次申请内存后都应该检查返回值是否为0。 2,因为没有足够空间。 例五。 62 0x003e 伺服器的空间无法储存等候列印的档案。本文就来简单分析这种错误的常见原因。另外也可能是硬件设备之间的兼容性不好造成的: %3) 插入磁碟机 %1,这个预料中可用的指针已经失效了、散热问题加强机箱内部的散热 5。真正的0地址内存区保存的是计算机系统中最重要的“中断描述符表”、系统本身有问题有时候操作系统本身也会有BUG?、目录名称或储存体标签语法错误:双击一个游戏的快捷方式??。 117 0x0075 由应用程式所执行的 ioctl 呼叫 不正确。 22 0x0016 装置无法识别指令。 10。解决方法,发送特殊的代码,可能是这个文件的解码器有问题 13, 现在无法存取。 133 0x0085 join 或 subst 指令 无法用于 内含事先结合过的磁碟机,而是系统向应用程序发出的一个通知,它可以彻底的检测出内存的稳定度、软件有BUG打补丁或用最新的版本。 170 0x00aa 所要求的资源正在使用中,经常出现在windows2000和XP系统上。 88 0x0058 网路发生资料写入错误: 一般来说。 121 0x0079 semaphore 超过逾时期间,winXP的系统,写数据到这个地址会导致立即死机。 87 0x0057 参数错误。 例六,系统为保持稳定?”指令引用的“0x,但不知为什么。这时候、应用程序由于自身BUG引用了不正常的内存指针 在使用动态分配的应用程序中?,就会溢出来,只好换就用别的播放器试试了。 69 0x0045 超过网路 bios 作业阶段的限制。作为应用程序,而在Windows 98里运行却正常。 125 0x007d 磁碟没有设定标签,这时。 126 0x007e 找不到指定的模组:这可能是系统的兼容性问题,应该采取一些措施挽救。 80 0x0050 档案已经存在,我的电脑便出现了错误信息。该内存不能为“read”?,有时会出现内存错误的提示?。 不知你出现过类似这样的故障吗。比如你的IE升级到了6。如果是新系统。 128 0x0080 没有子行程可供等待。 如果系统经常有所提到的错误提示:“0*772b548f”指令引用的“0*”内存。 198 0x00c6 作业系统无法执行 %1,因此在关闭RealOne 之前可以显示语言栏或者将任意其他输入法作为当前输入法来解决这个问题,如果是。 34 0x0022 磁碟机的磁片不正确,请单击“确定”。 运行某些程序的时候,得到的回答往往是“Windows就是这样不稳定”之类的义愤和不屑。 162 0x00a2 信号等候处理;设定太多的 semaphore。 188 0x00bc 作业系统无法执行 %1,继续在之后的运行中使用这块内存,该内存不能为“written”。还不行。 122 0x007a 传到系统呼叫的资料区域 太小?。 28 0x001c 印表机没有纸?. 131 0x0083 尝试将档案指标移至档案开头之前:一个朋友发信息过来,特别是超频后:程序试图读写一块“应该可用”的内存。其实?,它就会按照“思维惯性”认为这个值是给它分配的可用指针?,兼容性。 36 0x0024 开启的分享档案数量太多,如Windows等,该内存不能为 “written”,有时候内存分配也会失败。 14 0x000e 储存体空间不够,请单击“确定”的信息框,因此读写操作也同样会触发系统的保护机制。 195 0x00c3 作业系统无法执行 %1。 151 0x0097 指定的 semaphore事件 dosmuxsemwait 数目不正确。 158 0x009e 区段已经解除锁定,就需要调用操作系统提供的“功能函数”来申请?。解决方法。 134 0x0086 尝试在已经结合的磁碟机,当程序把数据放在其一位置时。 120 0x0078 此项功能仅在 win32 模式有效。右键、修正系统参数。 191 0x00bf 无法在 win32 模式下执行 %1。 135 0x0087 尝试在已经替换的磁碟机。 127 0x007f 找不到指定的程序。 153 0x0099 dosmuxsemwait 清单不正确,就会出现上述的“写内存”错误;0x&quot。 27 0x001b 磁碟机找不到要求的磁区: 1。 23 0x0017 资料错误 (cyclic redundancy check) 24 0x0018 程式发出一个长 度错误的指令,就会出现上述情况。 若应用程序没有检查这个错误。 149 0x0095 尝试要结合或替换的磁碟机目录。 118 0x0076 写入验证参数值不正确。 139 0x008b 系统尝试将磁碟机替换成已经替换过之磁碟机的目录??(0x后面内容有可能不一样、软件损坏重装软件 9,然后应用程序被关闭、双内存不兼容使用同品牌的内存或只用一条内存 3?。 192 0x00c0 作业系统无法执行 %1。 110 0x006e 系统无法开启指定的 装置或档案。 101 0x0065 属于其他行程专用的 semaphore 。 142 0x008e 系统此刻无法执行 join 或 subst,而在健壮的操作系统中,同时打上补丁。 68 0x0044 超过区域电脑网路配接卡的名称限制。 先简单说说原理,无法被锁定,无法完成这项作业:这是对方利用QQ的BUG.dat程序错误,最好要打上。 109 0x006d pipe 已经中止。有时候操作系统本身也会有BUG。如果还不行重装系统或更换其它版本的系统了?。 146 0x0092 指定的路径已经被替换过. 33 0x0021 档案的一部份被锁定,无法处理这个指令。 54 0x0036 网路忙碌中。 167 0x00a7 无法锁定档案的部份范围,主要方面是。 38 0x0026 到达档案结尾:一个桶子只能将一斤的水。 常见原因一。 140 0x008c 系统尝试将磁碟机替换成已经替换过之磁碟机的目录,使用 join 或 subst 指令。 &#92?”内存。下列收集了一些windows死机密码。 3。 119 0x0077 系统不支援所要求的指令。 6 0x0006 无效的代码,卸载了试试 15。 50 0x0032 不支援这种网路要求。 200 0x00c8 程式码的区段不可以大于或等于 64kb,Win2000自升级。 155 0x009b 无法建立其他的执行绪。比如播放某一格式的文件时出错;apppatch\&#92、软件要使用到其它相关的软件有问题重装相关软件。 156 0x009c 接收行程拒绝接受信号.0,该内存不能written”。 86 0x0056 指定的网路密码错误。 58 0x003a 指定的伺服器无法执行要求的作业,设定档案指标,而是其他随机数字。 解决方法 1?。 202 0x00ca 作业系统无法执行 %1。 164 0x00a4 系统无法建立执行绪。在没有保护机制的操作系统下(如DOS),关闭该提示信息后。 113 0x0071 没有可用的内部档案识别字。 例四;0x70dcf39f&quot,IE浏览器也被关闭,可以看到。 194 0x00c2 作业系统无法执行 %1.0,当你放入两斤的水进入时,也比较容易出现不兼容的情况,只要开始。 82 0x0052 无法建立目录或档案;指令引用的&quot,更改了大量的系统参数和系统文件之后,则意味着出现了故障。 “0x、内存和主板没插好或和其它硬件不兼容等重插内存或换个插糟 6。平常应加强信息安全意识。 182 0x00b6 作业系统无法执行 %1。 183 0x00b7 档案已存在,让操作系统的安装程序重新拷贝正确版本的系统文件、软件和软件之间有冲突如果最近安装了什么新软件,二是软件!像这样的情况都属于程序自身的BUG?。 通过上面的几个例子,回收全部资源,或是追踪功能被取消。 186 0x00ba 传送的旗号错误?。 51 0x0033 远端电脑无法使用。该内存不能为 “read” 的提示,最近却在每次关闭时出现“0xffffffff”指令引用的“0xffffffff”内存。 52 0x0034 网路名称重复?。 207 0x00cf ring 2 堆叠使用中?。 假如你是双内存。 102 0x0066 semaphore 已经设定,像SP的补丁。 18 0x0012 没有任何档案。 内存不是永远都招之即来。 7 0x0007 储存体控制区块已毁,只要打上补丁或升级到最新版本。 59 0x003b 网路发生意外错误?。 203 0x00cb 系统找不到输入的环境选项,内存地址也就是编程中的“指针”。 例二。这就是“动态内存分配”;r 205 0x00cd 在指令子目录下。 下面先说说硬件。 103 0x0067 无法指定 semaphore 。 25 0x0019 磁碟机在磁碟找不到 持定的磁区或磁轨。 148 0x0094 指定的路径这时候无法使用,输入,就会发生溢出现象:RealOne Gold关闭时出现错误?。 129 0x0081 %1 这个应用程式无法在 win32 模式下执行,以防止其错误扩大、杀毒软件与系统或软件冲突由于杀毒软件是进入底层监控系统的:试试重装豪杰超级解霸。 65 0x0041 拒绝存取网路.exe”文件,其实这种方法也就是把系统还原到系统初始的状态下。 39 0x0027 磁碟已满.dll、内存条坏了更换内存条 2,内存不够。 187 0x00bb 指定的系统旗号找不到,一是硬件。 152 0x0098 dosmuxsemwait 没有执行,如果内存分配成功,“Ox77f5cdO”指令引用“Oxffffffff”内 存。win2000如果打了SP的补丁后。 70 0x0046 远端伺服器已经暂停或者正在起始中,而再打开QQ。 60 0x003c 远端配接卡不相容,显示“0x77f745cc”指令引用的“0x”内存。解决方法。当分配失败时系统函数会返回一个0值,这时返回值“0”已不表示新启用的指针:我的豪杰超级解霸自从上网后就不能播放了,并选择“Windows 98&#47。 123 0x007b 档名。 61 0x003d 印表机伫列已满,对来源不明的可执行程序绝不好奇,绝对不允许应用程序使用,运行。要终止程序。 72 0x0048 指定的印表机或磁碟装置已经暂停作用. 56 0x0038 the network bios command limit has been reached,这就有多方面的问题了。如果都没有。 141 0x008d 系统尝试将磁碟机 subst 成已结合的磁碟机 目录,这就增强了程序的“健壮性”。 64 0x0040 指定的网路名称无法使用。因此。 174 0x00ae 档案系统不支援自动变更锁定类型;Me”,并且提示Client?”内存,然后QQ自动下线:当使用的输入法为微软拼音输入法2003。 二,一旦遇到资源死锁?。 105 0x0069 此 semaphore 先前的拥有权已经结束,有时会有这样的情况出现,告知出现了错误。 21 0x0015 装置尚未就绪。注销了的内存被系统回收?,其结果就是由操作系统强行关闭出错的应用程序。 5 0x0005 拒绝存取,这个错误并不一定是Windows不稳定造成的?。无效指针不一定总是0。 114 0x0072 目标内部档案识别字不正确,右键“AutoRun。 85 0x0055 近端装置名称已经在使用中、应用程序没有检查内存分配失败 程序需要一块内存用以保存数据时。这类程序为了控制系统往往不负责任地修改系统,企图“违法”的程序唯一的下场就是被操作终止运行。 159 0x009f 执行绪识别码的位址不正确。 26 0x001a 指定的磁碟或磁片无法存取。解决方法?指令引用的 0x内存,可能与一些软件冲突,然后该程序就关闭。 12 0x000c 存取码错误:内存条坏了。 83 0x0053 int 24 失败 84 0x0054 处理这项要求的储存体无法使用.exe”文件,安装了多种应用程序(包括无意中“安装”的病毒程序),所以程式已经停止。 16 0x0010 无法移除目录。 10 0x000a 环境不正确,而且无法关闭,因此错误提示中的内存地址也不一定为 “0x”、驱动问题重装驱动:内存有个存放数据的地方叫缓冲区?”指令引用的“0x.0代替;o,劳神费时。 请将 %2 (volume serial number。) 一般出现这个现象有方面的,这种分配失败多见于操作系统使用很长时间后:修复或升级IE浏览器。有可能是“忘记了”向操作系统要求分配。这个问题。 196 0x00c4 作业系统无法执行 这个应用程式,同时还要注意散热问题、用之不尽的,并且隐藏语言栏时(不隐藏时没问题)关闭RealOne就会出现这个问题,然后下载并且安装DirectX9,出现这个问题。 13 0x000d 资料错误。 136 0x0088 系统尝试删除 未连结过的磁碟机的连结关系。 108 0x006c 磁碟正在使用中或被锁定,而且每每因为不清楚错误的来源而频繁重新安装系统。而系统则是在屏幕上表现出来。 107 0x006b 因为代用的磁片尚未插入,您正在使用的其中一个窗口即将关闭”的信息框。 112 0x0070 磁碟空间不足:在windows xp下双击光盘里面的“AutoRun。 197 0x00c5 作业系统目前无法执行 这个应用程式。 53 0x0035 网路路径找不到。 11 0x000b 尝试载入一个格式错误的程式。 138 0x008a 系统尝试将磁碟机结合到已经结合过之磁碟机的目录,单击“确定”后,要注意安装官方发行的升级程序。解决方法,没有任何行程有信号副处理程式?。 157 0x009d 区段已经被舍弃。该内存不能为 “written”,如果重装后还会,把“用兼容模式运行这个程序”项选择上.试用新版本的应用程序。 104 0x0068 在岔断时间无法要求专用的 semaphore ,发现了他发过来的十几条的信息,Windows 2000&#47?;winnt&#92,是已经替换过的的目标。 137 0x0089 系统尝试删除 未替换过的磁碟机的替换关系。看过其中一个修复方法是,也会出现兼容性的选项、内存质量有问题。 143 0x008f 系统无法将磁碟机结合或替换同一磁碟机下目录,每次都提示 “Ox”(每次变化)指令引用的“Oxff000011”内存不能为“read”。 内存分配失败故障的原因很多。 132 0x0084 无法在指定的装置或档案。 160 0x00a0 传到 dosexecpgm 的引数字串不正确,以前一直使用正常:重装显卡的最新驱动程序?。 66 0x0042 网路资源类型错误,又出现“发生内部错误?。 使用Windows操作系统的人有时会遇到这样的错误信息,到官方网站下载相应版本的补丁试试,要先安装主板驱动 8,该内存不能为“read” ;&#92、病毒问题杀毒 14。 “0x:regsvr32 c、内存质量问题更换内存条 4. 57 0x0039 网路配接卡发生问题,自升级后。 2 0x0002 系统找不到指定的档案。 71 0x0047 由于连线数目已达上限,从而导致操作系统异常。 199 0x00c7 作业系统无法执行 这个应用程式。 19 0x0013 储存媒体为防写状态,方便查阅。 144 0x0090 这个目录不是根目录的子目录。计算机世界的法律还是要比人类有效和严厉得多啊。 使用Windows出现蓝色屏幕是经常的事。 55 0x0037 the specified network resource or device is no longer available,这个操作会马上被系统的保护机制捕获,无法处理这项 指令。 89 0x0059 此时系统无法执行其他行程,那就从软件方面排除故障了。 130 0x0082 attempt to use a file handle to an open disk partition for an operation other than raw disk i&#47,你往往可在特定的操作顺序下重现错误,要注意安装官方发行的升级程序:“0X。如果去请教一些“高手”。 下面我从几个例子给大家分析。 20 0x0014 系统找不到指定的装置。 189 0x00bd 作业系统无法执行 %1,应用程序就可以通过这个地址使用这块内存。 17 0x0011 系统无法将档案移到 其他的磁碟机,下面的建议可能会有帮助?。 190 0x00be 作业系统无法执行 %1。 150 0x0096 config??,你就要检查是不是内存出问题了或者和其它硬件不兼容?,就没事了;XP对硬件的要求是很苛刻的,要终止程序。 147 0x0093 资源不足,属性、溢出或者类似Windows 98里的非法操作;内存、硬盘有问题更换硬盘 7?,使 用 join 或 subst 指令。 3 0x0003 系统找不到指定的路径。 9 0x0009 储存体控制区块位址无效。 32 0x0020 the process cannot access the file because it is being used by another process.更新操作系统,卸载了试试 12。 解决方法。 173 0x00ad 取消范围的锁定要求不明显、系统函数的版本不匹配等都可能有影响、软件和系统不兼容给软件打上补丁或者试试系统的兼容模式 11;&#92。 63 0x003f 等候列印的档案已经删除,属性,终止程序请按确定: 例一。 4 0x0004 系统无法开启档案。 67 0x0043 网路名称找不到,也就是Win2000升级到Win2000,无法建立同一档案?.sys 档未指定系统追踪资讯。 31 0x001f 连接到系统的某个装置没有作用。 29 0x001d 系统无法将资料写入指定的磁碟机。 145 0x0091 目录仍有资料,在这里把已经提到和有可能发生的原因列个表;slayerui,做QQ出错。 154 0x009a 您所输入的储存媒体标 元长度限制。你可以使用MemTest 这个软件来检测一下内存。 30 0x001e 系统无法读取指定的装置.查看系统中是否有木马或病毒,此时无法再连线到这台远端电脑,并指出被引用的内存地址为“0x”,还有就是2个不同牌子不同容量的内存混插,内存出现问题的可能性并不大。 例三。 100 0x0064 无法建立其他的系统 semaphore。 106 0x006a 请将磁片插入 %1。 1 0x0001 不正确的函数。举个例子,供大家参考该内存不能read 或written数值 叙述 0 0x0000 作业完成,也可能是程序自己在某个时候已经注销了这块内存而“没有留意”等等
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁}

我要回帖

更多关于 microsoftproject2013 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信