当前位置:文档之家› 内存泄露

内存泄露

内存泄露
内存泄露

请问C#编写程序时,哪些代码属于托管代码,哪些代码属于非托管代码?

你用.NET的类库的应该都属于托管代码

调用一些COM,API的话是非托管的吧

以运行库为目标的代码称为托管代码,而不以运行库为目标的代码称为非托管代码。

在运行库的控制下执行的代码称作托管代码。相反,在运行库之外运行的代码称作非托管代码。COM 组件、ActiveX 接口和Win32 API 函数都是非托管代码的示例。

公共语言运行库概述

编译器和工具公开运行库的功能并使您能够编写受益于此托管执行环境的代码。使用针对运行库的语言编译器开发的代码称为托管代码;使用托管代码有很多好处,因为它具有诸多功能,如跨语言集成、跨语言异常处理、增强的安全性、版本控制和部署支持、简化的组件交互模型、调试和分析服务等。

若要使运行库能够向托管代码提供服务,语言编译器必须发出对代码中的类型、成员和引用进行描述的元数据。元数据与代码一起存储;每个可加载的公共语言运行库可移植可执行(PE) 文件都包含元数据。运行库使用元数据完成以下任务:查找和加载类,在内存中对实例进行布局,解析方法调用,生成本机代码,强制安全性,以及设置运行时上下文边界。

运行库自动处理对象布局和管理对对象的引用,当不再使用对象时释放它们。其生存期以这种方式来管理的对象称为托管数据。垃圾回收消除了内存泄漏以及其他一些常见的编程错误。如果代码是托管的,则可以在.NET Framework 应用程序中使用托管数据、非托管数据或者使用这两者。由于语言编译器提供它们自己的类型(如基元类型),因此可能并不总是知道(或需要知道)数据是否是托管的。

公共语言运行库使设计其对象能够跨语言交互的组件和应用程序变得很容易。用不同语言编写的对象可以互相通信,并且它们的行为可以紧密集成。例如,可以定义一个类,然后使用不同的语言从原始类派生类或调用原始类的方法。还可以将类的实例传递到用不同的语言编写的类的方法。这种跨语言集成之所以成为可能,是因为针对运行库的语言编译器和工具使用由运行库定义的通用类型系统,而且它们遵循运行库关于定义新类型以及创建、使用、保持和绑定到类型的规则。

所有托管组件都携带有关生成所基于的组件和资源的信息,作为其元数据的一部分。运行库使用这些信息确保组件或应用程序具有它需要的所有内容的指定版本,这使代码不太可能由于某些未满足的依赖项而中断。注册信息和状态数据不再保存在注册表(在注册表中建立和维护它们会很困难)中。相反,有关您定义的类型(和它们的依赖项)的信息作为元数据与代码存储在一起,这样大大降低了组件复制和移除任务的复杂性。

语言编译器和工具按照对于开发人员来说有用而直观的方式来公开运行库的功能。这意味着运行库的某些功能可能在一个环境中比在另一个环境中更引人注意。您对运行库的体验取决于您使用的语言编译器或工具。例如,如果您是一位Visual Basic 开发人员,您可能会注意到有了公共语言运行库,Visual Basic 语言的面向对象的功能比以前多了。下面是运行库的

一些优点:

性能改进。

能够轻松使用用其他语言开发的组件。

类库提供的可扩展类型。

新的语言功能,如面向对象的编程的继承、接口和重载;允许创建多线程的可缩放应用程序的显式自由线程处理支持;结构化异常处理和自定义属性支持。

如果使用Microsoft? Visual C++? .NET,则可以使用C++ 托管扩展来编写托管代码。C++ 托管扩展提供了托管执行环境以及对您所熟悉的强大功能和富于表现力的数据类型的访问等优点。其他运行库功能包括:

跨语言集成,特别是跨语言继承。

垃圾回收,它管理对象生存期,使引用计数变得不再必要。

自我描述的对象,它使得使用接口定义语言(IDL) 不再是必要的。

编译一次即可在任何支持运行库的CPU 和操作系统上运行的能力。

还可以使用C# 语言编写托管代码。C# 语言提供了下列优点:

完全面向对象的设计。

非常强的类型安全。

很好地融合了Visual Basic 的简明性和C++ 的强大功能。

垃圾回收。

类似于C 和C++ 的语法和关键字。

使用委托而不是函数指针,从而增强了类型安全和安全性。函数指针通过unsafe C# 关键字和C# 编译器(Csc.exe) 的/unsafe 选项可用于非托管代码和数据。

一般来说,.NET框架自带的类型绝大部分都只涉及托管资源,因此不必太多担心。但是只要是实现了IDisposable的.NET类型,都内部实现一般都使用了Win32的非托管资源。为了确保你在使用这些类型时候不至于造成非托管资源的泄漏,而且能够及时回收这些资源,微软特别设计了所谓的Dispose模式。

有可能的话,请参看CLR via C#或者Effective C#这样的书籍来了解相关的信息。对于自己设计类库的程序员来说,熟悉和掌握这些知识更加重要。

COM/COM++组件,ActiveX控件,API函数,指针运算,自制的资源文件...这些的非托管的,其它就是托管的.

在clr上编译运行的代码就是托管代码

非CLR编译运行的代码就是非托管代码

非托管代码用dispose free using 释放

大概吧

都是内存托管的,如果你要自己管理内存那就用

unsafe{} 关键字标注出一块区域,你可以在这里使用指针,管理内存,这里代码不托管。

最近刚做过些跨平台调用的,对托管和非托管的问题,了解得也比较深刻....

一般C#里面编写的代码的都是托管的代码,就是,对内存的管理,可以由平台本身帮你控制,而VC里面的代码则为非托管代码,它们所分配的内存空间必须由本身来释放....

另外,从非托管代码,与托管的代码的转化,Marshal类是一个不得不深入研究的类...... 非常的奥妙!!!!

调试内存泄漏的应用程序

发现并防止托管代码中出现内存泄漏

(https://www.doczj.com/doc/6817073905.html,/ zh-cn/magazine/cc163491.as px)

James Kovacs

本文讨论:

?理解托管应用程序中的内存泄漏问题

?.NET 应用程序中所用的非托管内存?帮助 .NET 垃圾收集器发挥应有功效本文使用了以下技术: .NET Framework

下载本文中所用的代码:MemoryLeaks2007_01.exe(163 KB) 浏览在线代码

目录

.NET 应用程序中的内存

检测泄漏

堆栈内存泄漏

非托管堆内存泄漏

“泄漏”托管堆内存

总结

一提到托管代码中出现内存泄漏,很多开发人员的第一反应都认为这是不可能的。毕竟垃圾收集器(GC) 会负责管理所有的内存,没错吧?但要知道,垃圾收集器只处理托管内存。基于Microsoft? .NET Framework 的应用程序中大量使用了非托管内存,这些非托管内存既可以被公共语言运行库(CLR) 使用,也可以在与非托管代码进行互操作时被程序员显式使用。在某些情况下,垃圾管理器似乎在逃避自己的职责,没有对托管内存进行有效处理。这通常是由于不易察觉的(也可能是非常明显的)编程错误妨碍了垃圾收集器的正常工作而造成的。作为经常与内存打交道的程序员,我们仍需要检查自己的应用程序,确保它们不会发生内存泄漏并能够合理有效地使用所需内存。

.NET 应用程序中的内存

您大概已经知道,.NET 应用程序中要使用多种类型的内存,包括:堆栈、非托管堆和托管堆。这里我们需要简单回顾一下。

堆栈堆栈用于存储应用程序执行过程中的局部变量、方法参数、返回值和其他临时值。堆栈按照每个线程进行分配,并作为每个线程完成其工作的一个暂存区。垃圾收集器并不负责清理堆栈,因为为方法调用预留的堆栈会在方法返回时被自动清理。但是请注意,垃圾收集器知道在堆栈上存储的对象的引用。当对象在一种方法中被实例化时,该对象的引用(32 位或64 位整型值,取决于平台类型)将保留在堆栈中,而对象自身却存储于托管堆中,并在变量超出范围时被垃圾收集器收集。

非托管堆非托管堆用于运行时数据结构、方法表、Microsoft 中间语言(MSIL)、JITed 代码等。非托管代码根据对象的实例化方式将其分配在非托管堆或堆栈上。托管代码可通过调用非托管的Win32? API 或实例化COM 对象来直接分配非托管堆内存。CLR 出于自身的数据结构和代码原因广泛地使用非托管堆。

托管堆托管堆是用于分配托管对象的区域,同时也是垃圾收集器的域。CLR 使用分代压缩垃圾收集器。垃圾收集器之所以称为分代式,是由于它将垃圾收集后保留下来的对象按生存时间进行划分,这样做有助于提高性能。所有版本的 .NET Framework 都采用三代分代方法:第0 代、第 1 代和第 2 代(从年轻代到年老代)。垃圾收集器之所以称为压缩式,是因为它将对象重新定位于托管堆上,从而能够消除漏洞并保持可用内存的连续性。移动大型对象的开销很高,因此垃圾收集器将这些大型对象分配在独立的且不会压缩的大型对象堆上。有关托管堆和垃圾收集器的详细信息,请参阅Jeffrey Richter 所著的分为两部分的系列文章“垃圾收集器:Microsoft .NET Framework 中的自动内存管理”和“垃圾收集器-第 2 部分:Microsoft .NET Framework 中的自动内存管理”。虽然该文的写作是基于 .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?堆栈大小均为1MB。因此如果应用程序的Process/Private Bytes 定期增大1MB,同时 .NET CLR LocksAndThreads/# of current logical Threads 也相应增大,那么罪魁祸首很可能是线程堆栈泄漏。图1显示了(恶意的)多线程逻辑导致的不正确的线程清理示例。

Figure 1 清理错误线程

复制代码

using System;

using System.Threading;

namespace MsdnMag.ThreadForker {

class Program {

static void Main() {

while(true) {

Console.WriteLine(

"Press 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,然后尝试自联接。联接会导致调用线程停止等待另一线程的终止。这样该线程就会陷入一个类似于先有鸡还是先有蛋的尴尬局面之中—线程要等待自身的终止。在任务管理器下查看该程序,会发现每次按 时,其内存使用率会增长1MB(即线程堆栈的大小)。

每次经过循环时,Thread 对象的引用都会被删除,但垃圾收集器并未回收分配给线程堆栈的内存。托管线程的生存期并不依赖于创建它的Thread 对象。如果您只是因为丢失了所有与Thread 对象相关联的引用而不希望垃圾收集器将一个仍在运行的进程终止,这种不依赖性是非常有好处的。由此可见,垃圾收集器只是收集Thread 对象,而非实际托管的线程。只有在其ThreadProc 返回后或者自身被直接终止的情况下,托管线程才会退出(其线程堆栈的内存不会释放)。因此,如果托管线程的终止方式不正确,分配至其线程堆栈的内存就会发生泄漏。

非托管堆内存泄漏

如果总的内存使用率增加,而逻辑线程计数和托管堆内存并未增加,则表明非托管堆出现内存泄漏。我们将对导致非托管堆中出现内存泄漏的一些常见原因进行分析,其中包括与非托管代码进行互操作、终结器被终止以及程序集泄漏。

与非托管代码进行互操作:这是内存泄漏的起因之一,涉及到与非托管代码的互操作,例如在COM Interop 中通过P/Invoke 和COM 对象使用C 样式的DLL。垃圾收集器无法识别非托管内存,而正是在托管代码的编写过程中错误地使用了非托管内存,才导致内存出现泄漏。如果应用程序与非托管代码进行互操作,要逐步查看代码并检查非托管调用前后内存的使用情况,以验证内存是否被正

确回收。如果内存未被正确回收,则使用传统的调试方法在非托管组件中查找泄漏。

终结器被终止:当一个对象的终结器未被调用,并且其中含有用于清理对象所分配的非托管内存的代码时,会造成隐性泄漏。在正常情况下,终结器都将被调用,但是CLR 不会对此提供任何保证。虽然未来可能会有所变化,但是目前的CLR 版本仅使用一个终结器线程。请考虑这样一种情况,运行不正常的终结器试图将信息记录到脱机的数据库。如果该运行不正常的终结器反复尝试对数据库进行错误的访问而从不返回,则“运行正常”的终结器将永远没有机会运行。该问题会不时出现,因为这取决于终结器在终结队列中的位置以及其他终结器采取何种行为。

当AppDomain 拆开时,CLR 将通过运行所有终结器来尝试清理终结器队列。被延迟的终结器可阻止CLR 完成AppDomain 拆开。为此,CLR 在该进程上做了超时操作,随后将停止该终止进程。但是这并不意味着世界末日已经来临。因为通常情况下,大多数应用程序只有一个AppDomain,而只有进程被关闭才会导致AppDomain 的拆开。当操作系统进程被关闭,操作系统会对该进程资源进行恢复。但不幸的是,在诸如https://www.doczj.com/doc/6817073905.html, 或SQL Server? 之类的宿主情况下,AppDomain 的拆开并不意味着宿主进程的结束。另一个AppDomain 会在同一进程中启动。任何因自身终结器未运行而被组件泄漏的非托管内存都将继续保持未引用状态,无法被访问,并且占用一定空间。因为内存的泄漏会随着时间的推移越来越严重,所以这将带来灾难性的后果。

在 .NET 1.x中,唯一的解决方法是结束并重新启动该进程。.NET Framework 2.0 中引入了关键的终结器,指明在AppDomain 关闭期间,终结器将清理非托管资源并必须获得运行的机会。有关详细信息,请参阅Stephen Toub 的文章:“利用 .NET Framework 的可靠性功能确保代码稳定运行”。

程序集泄漏:程序集泄漏相对来说要常见一些。一旦程序集被加载,它只有在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"));

https://www.doczj.com/doc/6817073905.html,spaces.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 = https://www.doczj.com/doc/6817073905.html,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 将被序列化为:

复制代码

xmlns:xsd="https://www.doczj.com/doc/6817073905.html,/2001/XMLSchema">

5d49c002-089d-4445-ac4a-acb8519e62c9

John

Doe

有时有必要在不改变类名称的前提下改变根元素的名称。(要与现有架构兼容可能需要根元素名称。)因此Person 可能需要被序列化为。XmlSerializer 构造函数能够很方便地被重载,将根元素名称作为第二参数,如下所示:

复制代码

XmlSerializer serializer = new XmlSerializer(typeof(Person),

new XmlRootAttribute("PersonInstance"));

当应用程序开始对Person 对象进行序列化/反序列化时,一切运转正常,直至引发OutOfMemoryException。对XmlSerializer 构造函数的重载并不会对动态生成的程序集进行缓存,而是在每次实例化新的XmlSerializer 时生成新的临时程序集。这时应用程序以临时程序集的形式泄漏非托管内存。

要修复该泄漏,请在类中使用XmlRootAttribute 以更改序列化类型的根元素名称:

复制代码

[XmlRoot("PersonInstance")]

public class Person {

// code

}

如果直接将属性赋予类型,则XmlSerializer 对为类型所生成的程序集进行缓存,从而避免了内存的泄漏。如果需要对根元素名称进行动态切换,应用程序能够利用工厂对其进行检索,从而对XmlSerializer 实例自身进行缓存。

复制代码

XmlSerializer serializer = XmlSerializerFactory.Create(

typeof(Person), "PersonInstance");

XmlSerializerFactory 是我创建的一个类,它可以使用PersonInstance 根元素名称来检查Dictionary 中是否包含有用于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 代对象提升至更高一代,从而再次导致更高的内存使用率和收集成本。

还有一种情况,它与事件处理程序和回调一起发生并且更难理解。我将以https://www.doczj.com/doc/6817073905.html, 为例,但同样类型的问题也会发生在任何应用程序中。考虑一下执行一次开销很大的查询,然后等上 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

}

看上去正常的代码实际上隐含着严重的错误。所有这些https://www.doczj.com/doc/6817073905.html, Page 实例都变成了“永世长存”的对象。OnCacheItemRemoved 是一个实例方法,CacheItemRemovedCallback 委托中包含了一个隐式的“this”指针,这里的“this”即为Page 实例。该委托被添加至Cache 对象。这样,就会产生一个从Cache 到委托再到Page 实例的依赖关系。在进行垃圾收集时,可以一直从根引用(Cache 对象)访问Page 实例。这时,Page 实例(以及在呈现时它所创建的所有临时对象)至少需要等待五分钟才能被收集,在此期间,它们都有可能被提升至第 2 代。幸运地是,有一种简单

的方法能够解决该示例中的问题。请将回调函数变为静态。Page 实例上的依赖关系就会被打破,从而可以像第0 代对象一样以很低的开销来进行收集。

总结

我已经就 .NET 应用程序中能够导致内存泄漏或内存消耗过度的各种问题进行了讨论。虽然 .NET 可减少您对内存方面的关注程度,但是您仍必须关注应用程序的内存使用情况,以确保应用程序高效正常运行。虽然应用程序被托管,但这并不意味着您可以依靠垃圾收集器就能解决所有问题而将良好的软件工程实践束之高阁。虽然在应用程序的开发和测试阶段,您必须对其内存性能进行持续不断的监视。但是这样做非常值得。要记住,只有让用户满意才称得上是功能良好的应用程序。

答案是:即使在拥有GC的托管堆上,也有可能发生内存泄漏!

根据普遍意义上的内存泄漏定义,大多数的.NET内存对象在不再被使用后都会有短暂的一段时间的内存泄漏,因为要等待下一个GC时才有可能会被释放。但这种情况并不会对系统造成大的危害。

真正影响系统的严重内存泄漏情况如:

1:大对象的分配。

根据CLR的设计,.NET中的大对象将分配在托管堆内的一个特殊的区域,在回收大对象的时候,并不会像变通区域回收完成时要做内存碎片整理,这是因为这个区域都是大对象,对大对象的移动成本太大了。因此如果本来有三个连续的大对象,现在中间这个要释放掉了,然后新分配进来一个稍小点的大对象,这样势必在中间产生小的内存碎片,这个部分又无法利用。就造成了内存泄漏,并且除非碎片相邻的大对象被释放掉外,没法解决。因此在编程时要注意大对象的操作,尽量减少大对象的分配次数。

2:避免根引用对象的分配

所谓的根引用对象就是那些GC不会去释放的对象引用。比如类的公共静态变量。GC会视该变量对象在整个程序生命周期中都有效。因此就不会释放它。当它本身比较大,或者它内部又想用了其它很多对象时,这一连串的对象都无法在整个生命周期中得到释放。造成了较大的内存泄漏,应该时时注意这种风险的发生。

3:不合理的Finalize() 方法定义。

这个以后再进一步学习

[https://www.doczj.com/doc/6817073905.html,] 内存泄漏问题解决方法(转载)作者:flyfish 来源:博客园发布时间:2006-11-11 00:30 阅读:520 次原文链接[收藏]

https://www.doczj.com/doc/6817073905.html, 使用Sort 排序会出现内存泄漏问题,直到1.9.1-001-13Jul06 版本,该问题依然存在。

测试代码

private void MemoryLeak()

{

Directory directory = new RAMDirectory();

Analyzer analyzer = new StandardAnalyzer();

IndexWriter writer = new IndexWriter(directory, analyzer, true);

for (int i = 1; i <= 1000; i++)

{

Document document = new Document();

document.Add(Field.Keyword("field1", i.ToString()));

writer.AddDocument(document);

}

writer.Close();

int x = 0;

while (true)

{

IndexSearcher searcher = new IndexSearcher(directory);

//Sort sort = new Sort("field1"); // remove this line to avoid the memory leak

Sort sort = new Sort(new SortField[] { new SortField("field1", SortField.INT, false), SortField.FIELD_DOC });

Query query = QueryParser.Parse("1*", "field1", analyzer);

Hits hits = searcher.Search(query, sort);

searcher.Close();

Console.WriteLine("{1} - {0} MB", Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024, ++x);

}

}

解决方法

转自:

https://www.doczj.com/doc/6817073905.html,

解决lucene 1.* 使用排序后内存溢出问题

1. 在FieldSortedHitQueue (Search\FieldSortedHitQueue.cs) 类中增加如下方法。

internal static void Close(IndexReader reader)

{

lock (Comparators.SyncRoot)

{

System.Collections.Hashtable readerCache = (System.Collections.Hashtable)Comparators[reader];

if (readerCache != null)

{

readerCache.Clear();

}

Comparators.Remove(reader);

}

}

2. 在FieldCacheImpl (Search\FieldCacheImpl.cs) 类中增加如下方法。

public virtual void Close(IndexReader reader)

lock (this)

{

System.Collections.Hashtable readerCache = (System.Collections.Hashtable)cache[reader];

if (readerCache != null)

{

readerCache.Clear();

readerCache = null;

}

cache.Remove(reader);

}

}

3. 修改IndexSearcher (Search\IndexSearcher.cs) Close() 方法,增加两行代码。

public override void Close()

{

FieldSortedHitQueue.Close(reader);

https://www.doczj.com/doc/6817073905.html,.Search.FieldCache_Fields.DEFAULT.Close(reader);

if (closeReader)

reader.Close();

}

4. 为FieldCache Interface (Search\FieldCache.cs) 增加一个Close 方法。

public interface FieldCache

{

//...

void Close(IndexReader reader);

}

IIS内存溢出报错解决方案(一)

项目进行SSB改造以后,当客户端从服务器抓起大笔数据的时候,服务器报一个二进制流的错误,这个错误其实是一个内存溢出的错误。 提纲 故障现象 故障分析与解决 Code Review 工具与方法 故障现象 用户反映在进行数据导出时经常出现下面的错误:输入流是无效的二进制格式。开始内容(以字节为单位)是: 53-79-73-74-65-6D-2E-4F-75-74-4F-66-4D-65-6D-6F-72... 坏┏鱿指么砦蠛?/SPAN>,其他后面导出的用户都会出现该错误,导致无法进行操作。 故障分析 System.OutOfMemoryException 发生 53-79-73-74-65-6D-2E-4F-75-74-4F-66-4D-65-6D-6F-72... System.OutOfMemor ... System.OutOfMemoryException 发生的两种情况 应用程序消耗了过多的内存 内存碎片过多 内存Dump分析

有446M的free内存, 但最大的free内存块只有26M 不足64M 。内存碎片问题。 -------------------- Type SUMMARY -------------------------- TotSize ( KB) Pct(Tots) Usage 1b450000 ( 446784) : 21.30% : c940000 ( 206080) : 09.83% : MEM_IMAGE a3c000 ( 10480) : 00.50% : MEM_MAPPED 57824000 ( 1433744) : 68.37% : MEM_PRIVATE -------------------- State SUMMARY -------------------------- TotSize ( KB) Pct(Tots) Usage 2a82f000 ( 696508) : 33.21% : MEM_COMMIT 1b450000 ( 446784) : 21.30% : MEM_FREE 3a371000 ( 953796) : 45.48% : MEM_RESERVE Largest free region: Base 58bb0000 - Size 019f0000 (26560 KB) 内存中最大的一个dataset占用了18M内存,查看内容就是出现异常的导功能的内容sizeof(18e6a408) = 18,437,260 ( 0x119548c) bytes (System.Data.DataSet) … sizeof(18e6a8e0) = 18,437,260 ( 0x119548c) bytes (System.Data.DataTable) 系统中一共加载了6000多种Class,其中有3000多种是 0x0ff286b4 1 32 1 0x0ff2858c 1 32 1 0x0ff28464 1 32 1 0x0ff2833c 1 32 1 0x0ff28214 1 32 1 0x0ff280ec 1 32 1 0x0ff27fc4 1 32 1 0x0ff27e9c 1 32 1 0x0ff27d74 1 32 1 0x0ff27c4c 1 32 1 IIS日志分析 平均每天点击数:502,708 一共有 5,525 个IP访问过系统,平均每天有2,658 个访问 最大点击发生在 2007-11-19 达到 2,481,749次

内存泄露问题定位

内存泄露问题定位 The Standardization Office was revised on the afternoon of December 13, 2020

一、现象 进行24用户的常保,出现region1 bucket0的内存泄露,大概20分钟泄露1000个,保持3小时后内存耗尽,造成宕机。 二、定位方法 CCPU的内存分配机制如下: CCPU对于内存的处理,是采用事先划分好一大块静态内存区,供软件使用。其划分 对于内存分配,如果待分配的内存大小介于Bucket内存四种块大小之内,就使用相应的Bucket内存去分配;如果待分配的内存大小超出了Bucket内存最大块的大小,则使用Heap内存分配。 打开之前的定位内存泄露问题的宏开关,控制台出现大量打印。无法定位问题。 经分析,每块内存的分配回收的历史信息其实没有必要保存,只需要保留最后一个分配的情况即可。在每个块中,内存的管理其实是一个数组,用一个链表来记录未分配的内存块。根据这个原理,在分配、回收内存时可以得到该内存块在bucket中的索引。例如region1 bucket0有内存泄露,则分配一个10000的数组记录每个内存元素的分配回收情况。保存该内存元素当前是分配还是回收,最后分配的文件名行号。最后将这些信息输出到文件里。这种方法可以用于定位任何bucket的内存泄露,并且不需要PC侧的解析工具。 经过使用该方法分析,泄露的内存都是由解析上行语音包时分配的。 三、内存泄漏原因 下面分析这些内存为什么会泄露,经过初步走读代码在出现异常又没有释放内存时增加打印,但是进行常保测试没有出现这些打印。 在申请内存和数据包到达MAC时分别进行统计,发现申请内存的次数多于数据包到达MAC层的次数。 继续走查申请内存到数据包传递到MAC之间的代码,发现当tfi=0时也会申请内存,但后续因为计算出的TB个数为0,所以并没有得到处理,从而导致内存泄露。 四、结论 当用户数较多时会收到tfi为0的上行数据包,而代码中对这种情况的处理有问题。解决办法:在申请内存之前判断TB块总长度是否为0,如果为0则直接返回,不申请内存。

02-内存管理

1.怎么保证多人开发进行内存泄露的检查. 1>使用Analyze进行代码的静态分析 2>为避免不必要的麻烦, 多人开发时尽量使用ARC 2.非自动内存管理情况下怎么做单例模式. 创建单例设计模式的基本步骤· >声明一个单件对象的静态实例,并初始化为nil。 >创建一个类的类工厂方法,当且仅当这个类的实例为nil时生成一个该类的实例>实现NScopying协议, 覆盖allocWithZone:方法,确保用户在直接分配和初始化对象时,不会产生另一个对象。 >覆盖release、autorelease、retain、retainCount方法, 以此确保单例的状态。>在多线程的环境中,注意使用@synchronized关键字或GCD,确保静态实例被正确的创建和初始化。 3.对于类方法(静态方法)默认是autorelease的。所有类方法都会这样吗? 1> 系统自带的绝大数类方法返回的对象,都是经过autorelease的 4.block在ARC中和MRC中的用法有什么区别,需要注意什么 1.对于没有引用外部变量的Block,无论在ARC还是非ARC下,类型都是__NSGlobalBlock__,这种类型的block可以理解成一种全局的block,不需要考虑作用域问题。同时,对他进行Copy或者Retain操作也是无效的 2.应注意避免循环引用 5.什么情况下会发生内存泄漏和内存溢出? 当程序在申请内存后,无法释放已申请的内存空间(例如一个对象或者变量使用完成后没有释放,这个对象一直占用着内存),一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。内存泄露会最终会导致内存溢出! 当程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个int,但给它存了long才能存下的数,那就是内存溢出。 6.[NSArray arrayWithobject:] 这个方法添加对象后,需要对这个数组做释放操作吗? 不需要这个对象被放到自动释放池中 7.Json数据的解析,和解析数据的时候有内存泄露吗?有的话如何解 1>JSON解析的方案 ●SBJson ●JSONkit ●NSJSONSerialization 2>内存泄漏么?

内存泄漏检查

内存泄漏检测方法 ?对于不同的程序可以使用不同的方法来进行内存泄漏的检查,还可以使用一些专门的工具来进行内存问题的检查,例如MemProof、AQTime、Purify、BundsChecker 等。 ?也可以使用简单的办法:利用Windows自带的Perfmon来监控程序进程的handle count、Virtual Bytes和Working Set 3个计数器。 Handle Count记录了进程当前打开的句柄个数,监视这个计数器有助于发现程序是否存在句柄类型的内存泄漏; Virtual Bytes记录了程序进程在虚拟地址空间上使用的虚拟内存的大小,Virtual Bytes一般总大于程序的Working Set,监视Virtual Bytes可以帮助发现一些系统底层的问题; Working Set记录了操作系统为程序进程分配的内存总量,如果这个值不断地持续增加,而Virtual Bytes却跳跃式地增加,则很可能存在内存泄漏问题。 堆栈内存泄漏 ?堆栈空间不足会导致在受托管的情况下引发StackOverflowException类型的异常,线程泄漏是堆栈内存泄漏的其中一种。线程发生泄漏,从而使线程的整个堆栈发生泄漏。 ?如果应用程序为了执行后台工作而创建了大量的工作线程,但却没有正常终止这些线程,则可能会引起线程泄漏。 一个堆栈内存泄漏的例子: private void button1_Click(object sender, EventArgs e) { // 循环启动多个线程 for (int i = 0; i < 1500; i++) { Thread t = new Thread(new ThreadStart(ThreadProc)); t.Start(); } } static void ThreadProc() { Console.WriteLine("启动Thread #{0}

登入用友T3软件提示错误;“内存溢出”

登入用友T3软件提示错误;“内存溢出” 登入用友T3软件提示错误;“内存溢出” 系统缺少ufrtprn.ocx组件造成的。首先把c:\windows\system32\ufcomsql\ufrtprn.ocx 这个文件复制到其他地方,再用正常的文件(下面的附件)替换一下,然后重新注册,注册如下:如果操作系统是XP或2003,则:开始–运行 –regsvr32c:\windows\system32\ufcomsql\ufrtprn.ocx;如果操作系统是WINDOWS2000,则:开始–运行–regsvr32c:\winnt\system32\ufcomsql\ufrtprn.ocx。如果还是不行,那么就建议重新安装软件了。 服务异常了,可能是多种原因造成的,你可以在C:\Windows\System32\UF2000.log,打开UF2000.log查看错误详情再处理,如果你不太熟悉软件或者数据库的话,建议把用友安装目录下的ADMIN全部拷贝出来,然后重新安装软件,然后进行数据库附加即可 试一下: 1:执行系统管理,做初始化操作 2:若方法1未执行初始化,可能是这前做过初始化,开始-运行-regedit确定、找到注册表项:[HKEY_LOCAL_MACHINE\SOFTWARE\UFSoft\UF2000\2.0\Setup],右击删除Setup、再登录系统管理做初始化操作 方法3:若初始化操作建立系统数据库操作失败,可手工建立此系统数据库,还原用友通安装目录\Admin\ Ufsystem.bak文件,还原时数据库名称定义为UFSystem 重启”F8”,回车,进入安全模式,“高级启动选项”,找到“最后一次正确配置”

Js内存泄漏及解决方案

Js内存泄漏及解决方案 在IE下的JS编程中,以下的编程方式都会造成即使关闭IE也无法释放内存的问题,下面分类给出: 1、给DOM对象添加的属性是一个对象的引用。范例: var MyObject = {}; document.getElementById('myDiv').myProp = MyObject; 解决方法: 在window.onunload事件中写上: document.getElementById('myDiv').myProp = null; 2、DOM对象与JS对象相互引用。范例: function Encapsulator(element) { this.elementReference = element; element.myProp = this; } new Encapsulator(document.getElementById('myDiv')); 解决方法: 在onunload事件中写上: document.getElementById('myDiv').myProp = null; 3、给DOM对象用attachEvent绑定事件。范例: function doClick() {} element.attachEvent("onclick", doClick); 解决方法: 在onunload事件中写上: element.detachEvent('onclick', doClick); 4、从外到内执行appendChild。这时即使调用removeChild也无法释放。范例: var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv); 解决方法: 从内到外执行appendChild: var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); parentDiv.appendChild(childDiv); document.body.appendChild(parentDiv); 5、反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)。范例: for(i = 0; i < 5000; i++) { hostElement.text = "asdfasdfasdf";

JAVA内存溢出解决方案

JAVA内存溢出 解决方案 1. 内存溢出类型 1.1. https://www.doczj.com/doc/6817073905.html,ng.OutOfMemoryError: PermGen space JVM管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的。它和堆不同,运行期内GC不会释放空间。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。 PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。 一个最佳的配置例子:(经过本人验证,自从用此配置之后,再未出现过tomcat死掉的情况) set JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m 1.2. https://www.doczj.com/doc/6817073905.html,ng.OutOfMemoryError: Java heap space 第一种情况是个补充,主要存在问题就是出现在这个情况中。其默认空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。如果内存剩余不到40%,JVM就会增大堆到Xmx设置的值,内存剩余超过70%,JVM就会减小堆到Xms设置的值。所以服务器的Xmx和Xms设置一般应该设置相同避免每次GC后都要调整虚拟机堆的大小。假设物理内存无限大,那么JVM内存的最大值跟操作系统有关,一般32位机是1.5g到3g之间,而64位的就不会有限制了。

几个内存泄漏的例子

几个内存泄漏的例子 ?new和delete要成对使用 ?new和delete要匹配 经常看到一些C++方面的书籍中这样提及到内存泄漏问题,这样的说法的意思是比较明白,但对于初学C++程序员还是很难掌握,所以下面举几个反面的例子,希望对大家有帮助。 例一:错误处理流程中的return导致的内存泄漏 bool MyFun() { CMyObject* pObj = NULL; pObj = new CMyObject(); … if (…) return false; … if(…) return false; … if (pObj != NULL) delete pObj; return true; } 注意:红色字体部分的return之前没有释放pObj,导致内存泄漏。 例二:exception改变了程序的正常流程,导致内存泄漏 情况1: HRESULT MyFun() { HRESULT hr = S_OK; try { CMyObject* pObj = NULL; pObj = new CMyObject(); … if (…) { hr = E_FAIL; throw hr; } … if(…) {

hr = E_FAIL; throw hr; } … if (pObj != NULL) delete pObj; } catch (HRESULT& eHr) { } return hr; } 情况2: void OtherFun() // 可能是自己写的其他函数; // 也可能是其他人写的函数; // 也可能是系统的API; { … if(…) throw exception; … } bool MyFun() { CMyObject* pObj = NULL; pObj = new CMyObject(); … OtherFun(); … if (pObj != NULL) delete pObj; return true; } 注意:上面的两种情况中的throw行为将导致程序的正常流程,一旦有throw的动作发生,pObj对象将不会被正确释放(delete)。 例三:忘记释放系统API创建的资源,导致内存泄露 bool CMyClass::MyFun() { HANDLE hHandle = CreateEvent(NULL,FALSE,TRUE,NULL); … if (…)

weblogic内存溢出解决方法

彻底解决Weblogic报出https://www.doczj.com/doc/6817073905.html,ng.OutOfMemoryError: PermGen space问题: 打开域下面的bin目录(D:\Oracle\Middleware\user_projects\domains\base_domain\bin)。 编辑setDomainEnv.cmd文件,将以下蓝色的地方设置内存大小改成自己需要的。 set WLS_HOME=%WL_HOME%\server if "%JA V A_VENDOR%"=="Sun" ( set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m ) else ( set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m ) set MEM_ARGS_64BIT=%WLS_MEM_ARGS_64BIT% set MEM_ARGS_32BIT=%WLS_MEM_ARGS_32BIT% if "%JA V A_USE_64BIT%"=="true" ( set MEM_ARGS=%MEM_ARGS_64BIT% ) else ( set MEM_ARGS=%MEM_ARGS_32BIT% ) set MEM_PERM_SIZE_64BIT=-XX:PermSize=128m set MEM_PERM_SIZE_32BIT=-XX:PermSize=48m if "%JA V A_USE_64BIT%"=="true" ( set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT% ) else ( set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT% ) set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=256m set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=128m

JAVA内存泄露专题

内存泄露与内存溢出 1定义 1、内存泄漏:一般可以理解为系统资源(各方面的资源,堆、栈、线程等)在错误使用的情况下,导致使用完毕的资源无法回收(或没有回收),从而造成那部分内存不可用的情况。 2、内存溢出:指内存不够使用而抛出异常,内存泄露是其形成的原因之一。 2危害 会导致新的资源分配请求无法完成,引起系统错误,最后导致系统崩溃。 3内存泄漏分类 4 内存泄露/溢出发生的区域

5内存溢出异常 6内存溢出常见原因 7发生内存泄露的情形Java内存泄露根本原因是什么呢?

答:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。 具体主要有如下几大类: 7.1 静态集合类引起内存泄露 像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。 例: 解析: 在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。 7.2创建过大对象

以上代码运行时瞬间报错。 7.3监听器 在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。 7.4 各种连接 比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。 7.5 内部类和外部模块等的引用 内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如: public void registerMsg(Object b); 这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。 7.6 单例模式 不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露

apache服务器出现内存溢出的解决方法

apache服务器出现内存溢出的解决方法 2011-10-08 14:26 Tomcat内存溢出的原因 在生产环境中tomcat内存设置不好很容易出现内存溢出。造成内存溢出是不一样的,当然处理方式也不一样。 这里根据平时遇到的情况和相关资料进行一个总结。常见的一般会有下面三种情况: 1.OutOfMemoryError: Java heap space 2.OutOfMemoryError: PermGen space 3.OutOfMemoryError: unable to create new native thread. Tomcat内存溢出解决方案 对于前两种情况,在应用本身没有内存泄露的情况下可以用设置tomcat jvm参数来解决。(-Xms -Xmx -XX:PermSize -XX:MaxPermSize) 最后一种可能需要调整操作系统和tomcat jvm参数同时调整才能达到目的。 第一种:是堆溢出。 原因分析: JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。 在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。 Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。 没有内存泄露的情况下,调整-Xms -Xmx参数可以解决。 -Xms:初始堆大小 -Xmx:最大堆大小 但堆的大小受下面三方面影响:

一次内存泄露问题的排查

一次内存泄露问题的排查 系统对外提供的Solr查询接口,在来自外部调用的压力加大之后,就会出现solr查询报Read Timed Out的异常,从表面现象上看是此时solr核压力过大,无法响应过多的查询请求。 但实际上此时并发查询压力并不是很大,那么为何solr核会无法及时响应查询请求呢?首先用top查看了下load average,也是很低,也佐证了系统本身压力并不大。 然后,用jstack –l 查看那些cpu使用率过高的线程,发现全都是GC线程,说明GC过于频繁,而且耗时过长,导致应用线程被挂起,无法响应客户端发来的请求,这种情况就应该是有存在内存泄露的问题咯。

于是,就用jmap将进程的堆转储文件dump出来到heap.bin文件中JMap -dump:format=b,file=/tmp/heap.bin 然后用Eclipse Memory Analyzer(MAT)打开堆转储文件进行分析通常我们都会采用下面的“三步曲”来分析内存泄露问题: 首先,对问题发生时刻的系统内存状态获取一个整体印象。 第二步,找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象

接下来,进一步去查看这个内存消耗大户的具体情况,看看是否有什么异常的行为。 下面将用一个基本的例子来展示如何采用“三步曲”来查看生产的分析报告。 如上图所示,在报告上最醒目的就是一张简洁明了的饼图,从图上我们可以清晰地看到一个可疑对象消耗了系统75% 的内存。 现在,让我们开始真正的寻找内存泄露之旅,点击“Leak Suspects”链接,可以看到如下图所示对可疑对象的详细分析报告。

Office2016 Excel的VBA打开显示内存溢出解决办法

Office2016 Excel的VBA打开显示内存溢出解决办法 1、在excel开发工具中打开查看代码显示内存溢出 刚安装完office2016,但是Excel中的Visual Basic却不能用。原因是 加载路径有问题,以前装了WPS软件,加载路径在WPS文件夹里面。都是WPS 搞的鬼,解决办法是,通过修改注册表的键值到VBE6EXT.OLB所在目录即可。2、解决方法 打开注册表:HKEY_CLASSES_ROOT\TypeLib{0002E157-0000-0000-C000-000000000046}\5.3\0\win32,我右侧数据显示加载路径是 “C:\Users\Administrator\AppData\Local\Kingsoft\WPS Office\10.1.0.5554\office6\vbe6ext.olb”将之修改为你VBE6EXT.OLB文件路径,我的是“C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB”(不知道在哪儿话,直接搜索就好了,VBA6不记得是否是我自己加的了,反正路径下有这个文件,在哪都一样。该方法实测有效) 3.其他方法 (1)卸载重装 点评:这个办法有时候管用,有时候也不管用,视具体情况而定,但个人不建议采用,因为这样的永远都让你学不到东西。 (2)移动VBE6EXT.OLB文件到C:\Program Files\Common Files\microsoft shared\VBA\VBA7 点评:“VBE6EXT.OLB”“VBA7”这两个文件在哪,一搜索便知,据说解决了部分的问题,但有的人电脑里没有VBA7这个文件夹,就无从下手了,亲测新建一个VBA7文件夹貌似也不可以,并非通用方法。

内存溢出和内存泄漏的区别

内存溢出和内存泄漏的区别(内存泄漏原因) 内存溢出out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。 内存泄露memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。 memory leak会最终会导致out of memory! 内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。 内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出. 以发生的方式来分类,内存泄漏可以分为4类: 1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。 3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。 4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。 从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。

系统应用服务器内存溢出解决报告

XXX系统应用服务器内存溢出解决报告 xxxx股份有限公司 2010.9

目录 第一章问题现象与分析 (2) 1.1、问题现象 (2) 1.2、通常导致这种现象的原因 (2) 1.3、xxx社保宕机现象对比分析 (3) 第二章解决方法路线图 (4) 2.1 jvm的调整 (4) 2.2 减少jvm内存使用 (5) 2.2.1 加快db访问速度,减少中间件并发业务量 (5) 2.2.2 限制sql返回结果集 (6) 2.2.3 减少业务会话中存放的对象 (6) 2.3 补救措施 (6) 第三章、解决结果与进一步建议 (6) 3.1 解决结果 (6) 3.2 进一步建议 (7) 第一章问题现象与分析 1.1、问题现象 XXX应用服务器经常有内存溢出、系统没有响应的现象,尤其在每月的月末最为明显。 目前的应用服务器有三种类型,其中ibm和linux应用服务器报告频繁出现内存溢出或没有响应的现象,hp unix应用服务器相稳定。在出现问题期间Weblogic无法响应任何客户端请求,大量请求加载到了这台没有响应的Server上,最后只有杀掉并重启这台应用服务器。 1.2、通常导致这种现象的原因 WLS Server 没响应可能的几种原因:

1、繁重的I/O,呼叫DB时间过长导致中间件内存耗尽,server没有响应。 2、程序死循环,loop-backs,这种情况cpu很忙,系统没有响应。 3、连接到外部server,没响应,由于网络等原因 4、2个以上的执行者同步死锁 5、业务量过大,全部线程都被占用,出现队列等待现象 6、读写本地I/O,发生阻塞 WLS Server 宕机的原因: OutOfMemory JNI程序 jvm的bug os的bug 1.3、xxx社保宕机现象对比分析 ?应用服务器没有响应分析 通过初步判断,对于xxx应用服务器没有响应的情况可以做如下排出法解决: ――程序死循环 这种情况会导致cpu非常繁忙,而通过目前观察,每次系统没响应的时候,cpu没有一直100%忙,另外,对出现问题时的java core分析没有发现这类线程,因此可以基本排除这种可能,。 ――连接到外部server,没响应,由于网络等原因 目前我们的业务基本都是直接通过中间件访问数据,没有通过应用服务器间调用或多数据库调用的,基本排除这种可能。 ――2个以上的执行者同步死锁 这种情况有可能,但比较难找,一般都是业务高峰的时候才有可能出现,跟应用人员了解后得知我们很少使用同步方式实现对资源的共享。另外通过对javacore进行分析,并未发现同步造成的死锁现象。 ――业务量过大,全部线程都被占用,出现队列等待现象 通过观察我们的业务量在高峰时确实很大,但由于我们配置的线程数都很高,尽管出现宕机时也没有达到配置的上线,所以这个方面可以被排除。 ――繁重的I/O,呼叫DB时间过长导致中间件内存耗尽 由于我们经常有新业务变更,尤其近期还有居民医保业务上线,因此I/O问题导致

C语言内存泄漏原因及对策分析

C语言中的内存泄漏原因及对策分析 引言: 在C语言程序设计中,内存泄漏几乎是很难避免的,C程序产生泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果产生覆盖内存,程序会变得非常脆弱,很容易受到恶意用户的攻击。内存泄漏是一种隐性危害,它们很难被发现,通常不能在相应的源代码中找到错误,需要仔细分析与专门的检测工具才能发现。 1、内存泄漏的定义 通常我们所说的内存泄漏,是指分配出去的内存在使用之后没有释放掉,没有回收,长此以往,会造成没有足够的内存可以分配。一般表现为运行时间越长,占用的内存越多,最终导致系统奔溃。 一般的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free 或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。 2、内存泄漏原因分析 内存泄漏的原因实质是没有释放向系统申请的内存,要了解内存泄漏产生的原因,我们首先了解C语言内存分配情况。 2.1 C语言内存分配情况 在C语言中,根据数据在内存中存在的时间(生存周期)不同,

将内存空间分为三个区: 1)程序区:用于存储程序的代码,即程序的二进制代码。 2)静态存储区:用于存储全局变量和静态变量,这些变量的空间在程序编译时就已经分配好了。 3)动态存储区:用于在程序执行时分配的内存,又分为:堆区(heap)和栈区(stack)。堆区:用于动态内存分配,程序运行时由内存分配函数在堆上分配内存。在C语言中,只能使用指针才能动态的分配内存。栈区:在函数执行时,函数内部的局部变量和函数参数的存储单元的内存区域,函数运行结束时,这些内存区域会自动释放。 2.2 C语言动态内存分配 在C语言中用内存分配函数来实现内存的动态分配,这些函数有:malloc()和realloc()等函数。malloc(): 使用这个函数时需要包含头文件。使用该函数需要指定要分配的内存字节数作为参数,例如: int *pNumber=(int *) malloc(100) 这条语句分配了100个字节的内存,并把这个内存块的地址赋给pNumber,这个内存块可以保存最大25个int值,每个int占4个字节。如果不能分配请求的内存,malloc()会返回一个null指针。 2.3 释放动态分配的内存 堆上分配的内存会在整个应用程序结束之后,由操作系统负责回收,但最好是在使用完这些内存后立即释放。如果不释放,会引起内存泄漏,极大占用系统资源,可能会产生各种未知的错误。所以,必

Java内存泄露模拟及分析解决方法

derwee Java内存泄露模拟及分析解决方法 1.1 实践目标: 1、使用JA V A代码实现模拟内存溢出 2、分析JDK内存溢出的原因 3、总结存在bug的JA V A编码实践 4、总结JVM优化的方法 1.2 模拟内存溢出: 为了方便模拟内存,特意把JVM的内存参数指定为更小(我的本本内存是8G的)。修改eclipse参数文件调用JVM参数: -vmargs -Xms40m(原始是-Xms40m) -Xmx100m(原始是-Xmx384m) 演示JA V A小程序实现原理:使用集合类对象装载大量的Persion对象,每次把new出来的对象加入集合类对象后,更改对象的属性,再从集合类对象中删除该对象。会出现该删除的对象没有被删掉,Persion类对象不断占用内存,导致分配给JVM的内存被耗光。 package .*; /** * * @ClassName: OutOfMemory * @Description: 内存溢出模拟,提出解决方法 * @author yangdw * @date 2012-3-25 下午6:58:49 */ public class OutOfMemory { public static void main(String[] args) { Collection collection = new HashSet(); for(int i=0;i<0;i++) { Persion per = new Persion(i,"yangdw"); (per);

1.2.1equals和hashcode重写原则[2] 1.2.1.1 对equals()应该遵循如下要求 1)对称性:如果(y)返回是“true”,那么(x)也应该返回是“true”。 2)自反性:(x)必须返回是“true”。 3)传递性:如果(y)返回是“true”,而且(z)返回是“true”,那么(x)也应该 返回是“true”。 4)任何情况下,(null),永远返回是“false”。 5)(和x不同类型的对象)永远返回是“false”。 1.2.1.2 hashCode()的返回值和equals()的关系如下 1)如果(y)返回“true”,那么x和y的hashCode()必须相等。 2)如果(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不 等。

Js内存泄漏及解决方案

在IE下的JS编程中,以下的编程方式都会造成即使关闭IE也无法释放内存的问题,下面分类给出: 1、给DOM对象添加的属性是一个对象的引用。范例: var MyObject = {}; document.getElementById('myDiv').myProp = MyObject; 解决方法: 在window.onunload事件中写上: document.getElementById('myDiv').myProp = null; 2、DOM对象与JS对象相互引用。范例: function Encapsulator(element) { this.elementReference = element; element.myProp = this; } new Encapsulator(document.getElementById('myDiv')); 解决方法: 在onunload事件中写上: document.getElementById('myDiv').myProp = null; 3、给DOM对象用attachEvent绑定事件。范例: function doClick() {} element.attachEvent("onclick", doClick); 解决方法: 在onunload事件中写上: element.detachEvent('onclick', doClick); 4、从外到内执行appendChild。这时即使调用removeChild也无法释放。范例: var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv); 解决方法: 从内到外执行appendChild: var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); parentDiv.appendChild(childDiv);

相关主题
文本预览
相关文档 最新文档