[ASP.NET] 24个优化ASP.NET的性能方法(转)

mikel阅读(671)

1. 数据库访问性能优化

数据库的连接和关闭

访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源。ASP.NET中提供 了连接池(Connection Pool)改善打开和关闭数据库对性能的影响。系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求。连接池的大小是 有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能。因此,在建立数据库连接后只有在真正需要操作时才打开连接,使用完毕后马上关闭, 从而尽量减少数据库连接打开的时间,避免出现超出连接限制的情况。

使用存储过程

存储过程是存储在服务器上的一组预编译的SQL语句,类似于DOS系统中的批处理文件。存储过程具有对数据库立即访问的功能,信息处理极为迅速。使用存储 过程可以避免对命令的多次编译,在执行一次后其执行规划就驻留在高速缓存中,以后需要时只需直接调用缓存中的二进制代码即可。另外,存储过程在服务器端运 行,独立于ASP.NET程序,便于修改,最重要的是它可以减少数据库操作语句在网络中的传输。

优化查询语句

ASP.NET中ADO连接消耗的资源相当大,SQL语句运行的时间越长,占用系统资源的时间也越长。因此,尽量使用优化过的SQL语句以减少执行时间。比如,不在查询语句中包含子查询语句,充分利用索引等。

2. 字符串操作性能优化

使用值类型的ToString方法

在连接字符串时,经常使用"+"号直接将数字添加到字符串中。这种方法虽然简单,也可以得到正确结果,但是由于涉及到不同的数据类型,数字需要通过装箱操 作转化为引用类型才可以添加到字符串中。但是装箱操作对性能影响较大,因为在进行这类处理时,将在托管堆中分配一个新的对象,原有的值复制到新创建的对象 中。使用值类型的ToString方法可以避免装箱操作,从而提高应用程序性能。

运用StringBuilder类

String类对象是不可改变的,对于String对象的重新赋值在本质上是重新创建了一个String对象并将新值赋予该对象,其方法ToString 对性能的提高并非很显著。在处理字符串时,最好使用StringBuilder类,其.NET 命名空间是System.Text。该类并非创建新的对象,而是通过Append,Remove,Insert等方法直接对字符串进行操作,通过 ToString方法返回操作结果。

  其定义及操作语句如下所示:

int num;

System.Text.StringBuilder str = new System.Text.StringBuilder(); //创建字符串

  str.Append(num.ToString()); //添加数值num

Response.Write(str.ToString); //显示操作结果3. 优化 Web 服务器计算机和特定应用程序的配置文件以符合您的特定需要

默认情况下,ASP.NET 配置被设置成启用最广泛的功能并尽量适应最常见的方案。因此,应用程序开发人员可以根据应用程序所使用的功能,优化和更改其中的某些配置,以提高应用程序的性能。下面的列表是您应该考虑的一些选项。

3.仅对需要的应用程序启用身份验证。

默认情况下,身份验证模式为 Windows,或集成 NTLM。大多数情况下,对于需要身份验证的应用程序,最好在 Machine.config 文件中禁用身份验证,并在 Web.config 文件中启用身份验证。根据适当的请求和响应编码设置来配置应用程序。ASP.NET 默认编码格式为 UTF-8。如果您的应用程序为严格的 ASCII,请配置应用程序使用 ASCII 以获得稍许的性能提高。

考虑对应用程序禁用 AutoEventWireup。

在 Machine.config 文件中将 AutoEventWireup 属性设置为 false,意味着页面不将方法名与事件进行匹配和将两者挂钩(例如 Page_Load)。如果页面开发人员要使用这些事件,需要在基类中重写这些方法(例如,需要为页面加载事件重写 Page.OnLoad,而不是使用 Page_Load 方法)。如果禁用 AutoEventWireup,页面将通过将事件连接留给页面作者而不是自动执行它,获得稍许的性能提升。

从请求处理管线中移除不用的模块。

默认情况下,服务器计算机的 Machine.config 文件中 节点的所有功能均保留为激活。根据应用程序所使用的功能,您可以从请求管线中移除不用的模块以获得稍许的性能提升。检查每个模块及其功能,并按您的需要自 定义它。例如,如果您在应用程序中不使用会话状态和输出缓存,则可以从 列表中移除它们,以便请求在不执行其他有意义的处理时,不必执行每个模块的进入和离开代码。

4. 一定要禁用调试模式

在部署生产应用程序或进行任何性能测量之前,始终记住禁用调试模式。如果启用了调试模式,应用程序的性能可能受到非常大的影响。

5. 对于广泛依赖外部资源的应用程序,请考虑在多处理器计算机上启用网络园艺

ASP.NET 进程模型帮助启用多处理器计算机上的可缩放性,将工作分发给多个进程(每个CPU一个),并且每个进程都将处理器关系设置为其 CPU。此技术称为网络园艺。如果应用程序使用较慢的数据库服务器或调用具有外部依赖项的 COM 对象(这里只是提及两种可能性),则为您的应用程序启用网络园艺是有益的。但是,在决定启用网络园艺之前,您应该测试应用程序在网络园中的执行情况。

6. 只要可能,就缓存数据和页输出

ASP.NET 提供了一些简单的机制,它们会在不需要为每个页请求动态计算页输出或数据时缓存这些页输出或数据。另外,通过设计要进行缓存的页和数据请求(特别是在站点 中预期将有较大通讯量的区域),可以优化这些页的性能。与 .NET Framework 的任何 Web 窗体功能相比,适当地使用缓存可以更好的提高站点的性能,有时这种提高是超数量级的。使用 ASP.NET 缓存机制有两点需要注意。首先,不要缓存太多项。缓存每个项均有开销,特别是在内存使用方面。不要缓存容易重新计算和很少使用的项。其次,给缓存的项分配 的有效期不要太短。很快到期的项会导致缓存中不必要的周转,并且经常导致更多的代码清除和垃圾回收工作。若关心此问题,请监视与 ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate 性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称作内存压力。

7. 选择适合页面或应用程序的数据查看机制

根据您选择在 Web 窗体页显示数据的方式,在便利和性能之间常常存在着重要的权衡。例如,DataGrid Web 服务器控件可能是一种显示数据的方便快捷的方法,但就性能而言它的开销常常是最大的。在某些简单的情况下,您通过生成适当的 HTML 自己呈现数据可能很有效,但是自定义和浏览器定向会很快抵销所获得的额外功效。Repeater Web 服务器控件是便利和性能的折衷。它高效、可自定义且可编程。

8. 将 SqlDataReader 类用于快速只进数据游标

SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法。如果当创建 ASP.NET 应用程序时出现允许您使用它的情况,则 SqlDataReader 类提供比 DataSet 类更高的性能。情况之所以这样,是因为 SqlDataReader 使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据。另外,SqlDataReader 类实现 IEnumerable 接口,该接口也允许您将数据绑定到服务器控件。有关更多信息,请参见 SqlDataReader 类。有关 ASP.NET 如何访问数据的信息,请参见通过 ASP.NET 访问数据。

9. 将 SQL Server 存储过程用于数据访问

在 .NET Framework 提供的所有数据访问方法中,基于 SQL Server 的数据访问是生成高性能、可缩放 Web 应用程序的推荐选择。使用托管 SQL Server 提供程序时,可通过使用编译的存储过程而不是特殊查询获得额外的性能提高。

10. 避免单线程单元 (STA) COM 组件

默认情况下,ASP.NET 不允许任何 STA COM 组件在页面内运行。若要运行它们,必须在 .aspx 文件内将 ASPCompat=true 属性包含在 @ Page 指令中。这样就将执行用的线程池切换到 STA 线程池,而且使 HttpContext 和其他内置对象可用于 COM 对象。前者也是一种性能优化,因为它避免了将多线程单元 (MTA) 封送到 STA 线程的任何调用。使用 STA COM 组件可能大大损害性能,应尽量避免。若必须使用 STA COM 组件,如在任何 interop 方案中,则应在执行期间进行大量调用并在每次调用期间发送尽可能多的信息。另外,小心不要在构造页面期间创建任何 STA COM 组件。例如下面的代码中,在页面构造时将实例化由某个线程创建的 MySTAComponent,而该线程并不是将运行页面的 STA 线程。这可能对性能有不利影响,因为要构造页面就必须完成 MTA 和 STA 线程之间的封送处理。

Dim myComp as new MySTAComponent() Public Sub Page_Load() myComp.Name = "Bob" End Sub

首选机制是推迟对象的创建,直到以后在 STA 线程下执行上述代码,如下面的例子所示。

Dim myComp Public Sub Page_Load() myComp = new MySTAComponent() myComp.Name = "Bob" End Sub

推荐的做法是在需要时或者在 Page_Load 方法中构造任何 COM 组件和外部资源。永远不要将任何 STA COM 组件存储在可以由构造它的线程以外的其他线程访问的共享资源里。这类资源包括像缓存和会话状态这样的资源。即使 STA 线程调用 STA COM 组件,也只有构造此 STA COM 组件的线程能够实际为该调用服务,而这要求封送处理对创建者线程的调用。此封送处理可能产生重大的性能损失和可伸缩性问题。在这种情况下,请研究一下使 COM 组件成为 MTA COM 组件的可能性,或者更好的办法是迁移代码以使对象成为托管对象。

 11. 将调用密集型的 COM 组件迁移到托管代码

  .NET Framework 提供了一个简单的方法与传统的 COM 组件进行交互。其优点是可以在保留现有投资的同时利用新的平台。但是在某些情况下,保留旧组件的性能开销使得将组件迁移到托管代码是值得的。每一情况都是 不一样的,决定是否需要迁移组件的最好方法是对 Web 站点运行性能测量。建议您研究一下如何将需要大量调用以进行交互的任何COM 组件迁移到托管代码。许多情况下不可能将旧式组件迁移到托管代码,特别是在最初迁移 Web 应用程序时。在这种情况下,最大的性能障碍之一是将数据从非托管环境封送到托管环境。因此,在交互操作中,请在任何一端执行尽可能多的任务,然后进行一个 大调用而不是一系列小调用。例如,公共语言运行库中的所有字符串都是 Unicode 的,所以应在调用托管代码之前将组件中的所有字符串转换成 Unicode 格式。另外,一处理完任何 COM 对象或本机资源就释放它们。这样,其他请求就能够使用它们,并且最大限度地减少了因稍后请求垃圾回收器释放它们所引起的性能问题。

  12. 在 Visual Basic .NET 或 JScript. 代码中使用早期绑定

  以往,开发人员喜欢使用 Visual Basic、VBScript. 和 JScript. 的原因之一就是它们所谓“无类型”的性质。变量不需要显式类型声明,并能够简单地通过使用来创建它们。当从一个类型到另一个类型进行分配时,转换将自动执 行。不过,这种便利会大大损害应用程序的性能。Visual Basic 现在通过使用 Option Strict 编译器指令来支持类型安全编程。为了向后兼容,默认情况下,ASP.NET 不启用该选项。但是,为了得到最佳性能,强烈建议在页中启用该选项。若要启用 Option Strict,请将 Strict 属性包括在 @ Page 指令中,或者,对于用户控件,请将该属性包括在 @ Control 指令中。下面的示例演示了如何设置该属性,并进行了四个变量调用以显示使用该属性是如何导致编译器错误的。

  JScript. .NET 也支持无类型编程,但它不提供强制早期绑定的编译器指令。若发生下面任何一种情况,则变量是晚期绑定的:被显式声明为 Object,是无类型声明的类的字段,是无显式类型声明的专用函数或方法成员,并且无法从其使用推断出类型。   最后一个差别比较复杂,因为如果 JScript. .NET 编译器可以根据变量的使用情况推断出类型,它就会进行优化。在下面的示例中,变量 A 是早期绑定的,但变量 B 是晚期绑定的。

  var A;

  var B;

  A = "Hello";

  B = "World";

  B = 0; 为了获得最佳的性能,当声明 JScript. .NET 变量时,请为其分配一个类型。例如,var A : String。

  13. 使请求管线内的所有模块尽可能高效

  请求管线内的所有模块在每次请求中都有机会被运行。因此,当请求进入和离开模块时快速地触发代码至关重要,特别是在不使用模块功能的代码路径里。分别在使用及不使用模块和配置文件时执行吞吐量测试,对确定这些方法的执行速度非常有用。
  

  14. 使用 HttpServerUtility.Transfer 方法在同一应用程序的页面间重定向

  采用 Server.Transfer 语法,在页面中使用该方法可避免不必要的客户端重定向。

  15. 必要时调整应用程序每个辅助进程的线程数

  ASP.NET 的请求结构试图在执行请求的线程数和可用资源之间达到一种平衡。已知一个使用足够 CPU 功率的应用程序,该结构将根据可用于请求的 CPU 功率,来决定允许同时执行的请求数。这项技术称作线程门控。但是在某些条件下,线程门控算法不是很有效。通过使用与 ASP.NET Applications 性能对象关联的 Pipeline Instance Count 性能计数器,可以在 PerfMon 中监视线程门控。当页面调用外部资源,如数据库访问或 XML Web services 请求时,页面请求通常停止并释放 CPU。如果某个请求正在等待被处理,并且线程池中有一个线程是自由的,那么这个正在等待的请求将开始被处理。遗憾的是,有时这可能导致 Web 服务器上存在大量同时处理的请求和许多正在等待的线程,而它们对服务器性能有不利影响。通常,如果门控因子是外部资源的响应时间,则让过多请求等待资源, 对 Web 服务器的吞吐量并无帮助。为缓和这种情况,可以通过更改 Machine.config 配置文件节点的 maxWorkerThreads 和 maxIOThreads 属性,手动设置进程中的线程数限制。

  注意:辅助线程是用来处理 ASP.NET 请求的,而 IO 线程则是用于为来自文件、数据库或 XML Web services 的数据提供服务的。分配给这些属性的值是进程中每个 CPU 每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。无论如何,对于有四个或八个 CPU 的计算机,最好更改默认值。对于有一个或两个处理器的计算机,默认值就可以,但对于有更多处理器的计算机的性能,进程中有一百或两百个线程则弊大于利。注 意进程中有太多线程往往会降低服务器的速度,因为额外的上下文交换导致操作系统将 CPU 周期花在维护线程而不是处理请求上。

  16. 适当地使用公共语言运行库的垃圾回收器和自动内存管理

  小心不要给每个请求分配过多内存,因为这样垃圾回收器将必须更频繁地进行更多的工作。另外,不要让不必要的指针指向对象,因为它们将使对象 保持活动状态,并且应尽量避免含 Finalize 方法的对象,因为它们在后面会导致更多的工作。特别是在 Finalize 调用中永远不要释放资源,因为资源在被垃圾回收器回收之前可能一直消耗着内存。最后这个问题经常会对 Web 服务器环境的性能造成毁灭性的打击,因为在等待 Finalize 运行时,很容易耗尽某个特定的资源。
  17. 如果有大型 Web 应用程序,可考虑执行预批编译

  每当发生对目录的第一次请求时都会执行批编译。如果目录中的页面没有被分析并编译,此功能会成批分析并编译目录中的所有页面,以便更好地利用磁 盘和内存。如果这需要很长时间,则将快速分析并编译单个页面,以便请求能被处理。此功能带给 ASP.NET 性能上的好处,因为它将许多页面编译为单个程序集。从已加载的程序集访问一页比每页加载新的程序集要快。批编译的缺点在于:如果服务器接收到许多对尚未编 译的页面的请求,那么当 Web 服务器分析并编译它们时,性能可能较差。为解决这个问题,可以执行预批编译。为此,只需在应用程序激活之前向它请求一个页面,无论哪页均可。然后,当用户 首次访问您的站点时,页面及其程序集将已被编译。没有简单的机制可以知道批编译何时发生。需一直等到 CPU 空闲或者没有更多的编译器进程(例如 csc.exe(C# 编译器)或 vbc.exe(Visual Basic 编译器))启动。还应尽量避免更改应用程序的 \bin 目录中的程序集。更改页面会导致重新分析和编译该页,而替换 \bin 目录中的程序集则会导致完全重新批编译该目录。在包含许多页面的大规模站点上,更好的办法可能是根据计划替换页面或程序集的频繁程度来设计不同的目录结 构。不常更改的页面可以存储在同一目录中并在特定的时间进行预批编译。经常更改的页面应在它们自己的目录中(每个目录最多几百页)以便快速编译。Web 应用程序可以包含许多子目录。批编译发生在目录级,而不是应用程序级。

  18. 不要依赖代码中的异常

  因为异常大大地降低性能,所以您不应该将它们用作控制正常程序流程的方式。如果有可能检测到代码中可能导致异常的状态,请执行这种操作。不要在 处理该状态之前捕获异常本身。常见的方案包括:检查 null,分配给将分析为数字值的 String 一个值,或在应用数学运算前检查特定值。下面的示例演示可能导致异常的代码以及测试是否存在某种状态的代码。两者产生相同的结果。

  try   {   result = 100 / num;   }   catch (Exception e)   {   result = 0;   }   // …to this.   if (num != 0)   result = 100 / num;   else   result = 0;

  19. 使用 HttpResponse.Write 方法进行字符串串联

  该方法提供非常有效的缓冲和连接服务。但是,如果您正在执行广泛的连接,请使用多个 Response.Write 调用。下面示例中显示的技术比用对 Response.Write 方法的单个调用连接字符串更快。

  Response.Write("a");   Response.Write(myString);   Response.Write("b");   Response.Write(myObj.ToString());   Response.Write("c");   Response.Write(myString2);   Response.Write("d"); 20. 除非有特殊的原因要关闭缓冲,否则使其保持打开

  禁用 Web 窗体页的缓冲会导致大量的性能开销。
  

  21. 只在必要时保存服务器控件视图状态

  自动视图状态管理是服务器控件的功能,该功能使服务器控件可以在往返过程上重新填充它们的属性值(您不需要编写任何代码)。但是,因为服务器控 件的视图状态在隐藏的窗体字段中往返于服务器,所以该功能确实会对性能产生影响。您应该知道在哪些情况下视图状态会有所帮助,在哪些情况下它影响页的性 能。例如,如果您将服务器控件绑定到每个往返过程上的数据,则将用从数据绑定操作获得的新值替换保存的视图状态。在这种情况下,禁用视图状态可以节省处理 时间。默认情况下,为所有服务器控件启用视图状态。若要禁用视图状态,请将控件的EnableViewState 属性设置为 false,如下面的 DataGrid 服务器控件示例所示。

  您还可以使用 @ Page 指令禁用整个页的视图状态。当您不从页回发到服务器时,这将十分有用:

  注意:@ Control 指令中也支持 EnableViewState 属性,该指令允许您控制是否为用户控件启用视图状态。若要分析页上服务器控件使用的视图状态的数量,请(通过将 trace="true" 属性包括在 @ Page 指令中)启用该页的跟踪并查看 Control Hierarchy 表的 Viewstate 列。有关跟踪和如何启用它的信息,请参见 ASP.NET 跟踪。

  22. 避免到服务器的不必要的往返过程

  虽然您很可能希望尽量多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些情况下却不宜使用 ASP.NET 服务器控件和回发事件处理。通常,只有在检索或存储数据时,您才需要启动到服务器的往返过程。多数数据操作可在这些往返过程间的客户端上进行。例如,从 HTML 窗体验证用户输入经常可在数据提交到服务器之前在客户端进行。通常,如果不需要将信息传递到服务器以将其存储在数据库中,那么您不应该编写导致往返过程的 代码。如果您开发自定义服务器控件,请考虑让它们为支持 ECMAScript. 的浏览器呈现客户端代码。通过以这种方式使用服务器控件,您可以显著地减少信息被不必要的发送到 Web 服务器的次数。

  使用 Page.IsPostBack 避免对往返过程执行不必要的处理

  如果您编写处理服务器控件回发处理的代码,有时可能需要在首次请求页时执行其他代码,而不是当用户发送包含在该页中的 HTML 窗体时执行的代码。根据该页是否是响应服务器控件事件生成的。

  使用 Page.IsPostBack 属性有条件地执行代码

  例如,下面的代码演示如何创建数据库连接和命令,该命令在首次请求该页时将数据绑定到 DataGrid 服务器控件。

  void Page_Load(Object sender, EventArgs e)   {   // Set up a connection and command here.   if (!Page.IsPostBack)   {   String query = "select * from Authors where FirstName like '%JUSTIN%'";   myCommand.Fill(ds, "Authors");   myDataGrid.DataBind();   }   }

  由于每次请求时都执行 Page_Load 事件,上述代码检查 IsPostBack 属性是否设置为 false。如果是,则执行代码。如果该属性设置为 true,则不执行代码。注意 如果不运行这种检查,回发页的行为将不更改。Page_Load 事件的代码在执行服务器控件事件之前执行,但只有服务器控件事件的结果才可能在输出页上呈现。如果不运行该检查,仍将为 Page_Load 事件和该页上的任何服务器控件事件执行处理。

  23. 当不使用会话状态时禁用它

  并不是所有的应用程序或页都需要针对于具体用户的会话状态,您应该对任何不需要会话状态的应用程序或页禁用会话状态。   若要禁用页的会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如:

  注意:如果页需要访问会话变量,但不打算创建或修改它们,则将@ Page 指令中的 EnableSessionState 属性设置为ReadOnly。还可以禁用 XML Web services 方法的会话状态。有关更多信息,请参见使用 ASP.NET 和 XML Web services 客户端创建的 XML Web services。若要禁用应用程序的会话状态,请在应用程序 Web.config 文件的 sessionstate 配置节中将 mode 属性设置为 off。例如:

  24. 仔细选择会话状态提供程序

  ASP.NET 为存储应用程序的会话数据提供了三种不同的方法:进程内会话状态、作为 Windows 服务的进程外会话状态和 SQL Server 数据库中的进程外会话状态。每种方法都有自己的优点,但进程内会话状态是迄今为止速度最快的解决方案。如果只在会话状态中存储少量易失数据,则建议您使用 进程内提供程序。进程外解决方案主要用于跨多个处理器或多个计算机缩放应用程序,或者用于服务器或进程重新启动时不能丢失数据的情况。有关更多信息,请参 见 ASP.NET 状态管理。

  25. 不使用不必要的Server Control

  ASP.net中,大量的服务器端控件方便了程序开发,但也可能带来性能的损失,因为用户每操作一次服务器端控件,就产生一次与服务器端的往返过程。因此,非必要,应当少使用Server Control。

  26. ASP.NET应用程序性能测试

  在对ASP.NET应用程序进行性能测试之前,应确保应用程序没有错误,而且功能正确。具体的性能测试可以采用以下工具进行:Web Application Strees Tool (WAS)是Microsoft发布的一个免费测试工具,可以从http://webtool.rte.microsoft.com/上下载。它可以模拟 成百上千个用户同时对web应用程序进行访问请求,在服务器上形成流量负载,从而达到测试的目的,可以生成平均TTFB、平均TTLB等性能汇总报告。 Application Center Test (ACT) 是一个测试工具,附带于Visual Studio.NET的企业版中,是Microsoft正式支持的web应用程序测试工具。它能够直观地生成图表结果,功能比WAS多,但不具备多个客户 机同时测试的能力。服务器操作系统"管理工具"中的"性能"计数器,可以对服务器进行监测以了解应用程序性能。

  结论:

  对于网站开发人员来说,在编写ASP.NET应用程序时注意性能问题,养成良好的习惯,提高应用程序性能,至少可以推迟必需的硬件升级,降低网站的成本。

[C#]关于--在 System.Threading.ThreadAbortException 中第

mikel阅读(757)

转载:http://www.cnblogs.com/wbcms/archive/2008/03/24/1119307.html
开发环境为VS2005,OS 为Windows 2003,系统登录后在跳转到另一页面时会报此错误:
在 System.Threading.ThreadAbortException 中第一次偶然出现的“mscorlib.dll”类型的异常
“System.Threading.ThreadAbortException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理
但不影响程序的正常运行。于是在网上查了查,发现相关资料不多。后来找到微软的官方解释,搞定。
————————————————————————————————————–

症状

如果使用 Response.EndResponse.Redirect Server.Transfer 方法,则出现 ThreadAbortException 异常。 可使用 try-catch 语句捕捉此异常。

原因

Response.End 方法停止页的执行,并将该执行变换到应用程序的事件管线中的 Application_EndRequest 事件。 Response.End 后面的代码行将不执行。
此问题出现在 Response.Redirect Server.Transfer 方法中,这是由于这两种方法都在内部调用 Response.End

解决方案

若要解决此问题,请使用下列方法之一:

  • 对于 Response.End,调用 ApplicationInstance.CompleteRequest 方法而不调用 Response.End,以便跳过 Application_EndRequest 事件的代码执行。
  • 对于 Response.Redirect,使用重载 Response.Redirect(String url, bool endResponse),对 endResponse 参数它传递 false以取消对 Response.End 的内部调用。例如:
      Response.Redirect ("nextpage.aspx", false);

    如果使用这种解决方法,Response.Redirect 后面的代码将得到执行。

  • 对于 Server.Transfer,请改用 Server.Execute 方法。

状态

这种现象是设计使然。

[C#] CLR Debugger - 在程序抛出异常的时候中断程序的执行

mikel阅读(934)

转载:http://blog.csdn.net/Donjuan/archive/2008/12/05/3454650.aspx

1.1.1.                       在程序抛出异常的时候中断程序的执行

.NET对异常的良好支持,让我们摆脱了以前在CC++程序开发过程中,那种不停的检查返回值的编程模式,使我们的程序变的更为简洁。异常不仅方便我们更高效的编写代码,同时也提供了一个好用的调试技巧当然需要调试器的良好支持。在程序开发的过程中,可能会经常碰到这种情况,程序在运行很长时间后,突然抛出一个异常以后,就中断执行了。这种错误(Bug)在平常执行时,一般很难重现,除了异常里面的堆栈信息以外,你几乎没有任何其他信息让你知道程序内部到底发生了什么。这是你就可以使用CLR Debugger里面的异常断点。

CLR Debugger里面,点击菜单栏里的“Debug”菜单里的“Exceptions…”菜单项,打开“Exceptions…”对话框设置异常断点:

 

 

1-6 CLR Debugger 异常断点设置对话框

在“Exceptions”对话框里面,勾上相应异常类型名旁边的复选框就可以设置,在这些异常抛出的时候,“Break when an exception is”列表中有两列复选框:“Thrown”和“User-unhandled”列,这两列的区别是:

l  当“User-unhandled”一列的复选框被选中以后,调试器只在程序中抛出指定的异常以后,并且程序中没有捕捉这个异常,调试器才中断程序的执行。而程序中有try…catch块捕捉该异常时,调试器会让程序继续执行,并忽略这个异常。

l  当“Thrown”一列的复选框被选中以后,则无论程序是否有try…catch块捕捉该异常,只要扔出了指定的异常,则调试器都会中断程序的执行。

 

小技巧:

选择命名空间旁边的复选框,会将该命名空间中定义的所有异常选中。

Exceptions”对话框中只列出了CLR当中定义的异常,如果你希望调试器在自定义的异常被抛出的时候,也能中断程序的执行,则你可以在“Exceptions”对话框中将自定义异常添加进来。点击“Add”按钮添加自定义异常,注意,你必须提供自定义异常的完整类名即命名空间加上类名。下面的示例代码演示了这一过程:

using System;

using System.IO;

 

namespace TestNamespace

{

    public class TestException : Exception

    {

    }

 

    public class ExceptionBreakpoint

    {

        static void Main()

        {

            // “New Exception”中添加TestNamespace.TestException异常

            // 并且在“Throw”中勾上“TestNamespace.TestException”复选框

            // 下面的语句才会导致程序中断,并且跳入到调试器当中

            try

            {

                for (int i = 0; i < 10; ++i)

                {

                    if (i == 5)

                        throw new TestException();

 

                    Console.WriteLine(i);

                }

            }

            catch (Exception e)

            {

                Console.WriteLine(e.Message);

            }

 

            // 无论“Exception”对话框中的“Throw”“User-unhandled”是否

            // 被选中,下面的语句总能导致程序中断,并且跳入到调试器中

            using (StreamReader reader = new StreamReader(

                "notexists.txt"))

                Console.WriteLine(reader.ReadToEnd());

        }

    }

}

1-2 异常断点的演示代码

在上面的例子当中,细心的读者会发现,当调试器捕获在try…catch块中抛出的异常时,高亮显示的当前代码并不是包含throw语句的那一行,而是throw下面的一行,而捕捉到using语句那一行抛出的异常后,高亮显示的当前代码行正是扔出异常的那一行。出现代码行偏差(姑且叫做代码行偏差吧)的原因会在后面的文章讲解调试器的工作原理时讲到。

Exceptions”对话框中还有一种异常类型:“Managed Debugging Assistants”是CLR 2.0新增加的特性,我们将会在讲解使用Visual Studio进行调试的时候讲到它。

[C#] 理解First Chance和Second Chance避免单步调试

mikel阅读(765)

转载:http://blog.csdn.net/Donjuan/archive/2009/02/02/3859160.aspx


在现在C++Java.Net代码大行其道的时候,很多代码错误(Bug)都是通过异常的形式表现出来的。由于工期紧或者种种原因,很多程序员在碰到程序发生未处理的异常的第一反应就是try … catch (Exception e) { … }。然而代码开发到后期的时候,这种简单粗暴的解决代码错误(Bug)的方式就会在其他不相干的地方表现出来,有的时候甚至导致程序随机的不稳定,而且很难调试。比如执行下面的代码你会发现输出的值是12345.6789

 

using System;

 

public class Class1

{

    public static void Main()

    {

        Console.WriteLine(Calculate("12345.6789 + 987654321l"));

    }

 

    private static double Calculate(string expression)

    {

        string[] numbers = expression.Split('+');

 

        return RedundantParseForDemoOnly(numbers[0].Trim()) +

               RedundantParseForDemoOnly(numbers[1].Trim());

    }

 

    private static double RedundantParseForDemoOnly(string number)

    {

        try

        {

            return double.Parse(number);

        }

        catch

        {

            return 0;

        }

    }

}

 

当然啦,你可以说只要强迫项目组所有程序员不要catch通用异常就可以了,但是老虎总是有打盹的时候,而且大部分项目组因为人才梯队建设的问题都会有那么几个新人……

 

我在工作中发现很多程序员都不理解,或者说根本就没有去注意过表2里面的输出,浪费了很多好的调试机会。因此我看到很多优秀的程序员都是非常辛苦的单步调试,一步步地跟踪代码。这个过程不仅辛苦,而且非常容易出错,因为单步跟踪代码容易让人犯晕。实际上你可以利用Windows提供的结构化异常处理(SEH)来帮助你快速找到问题所在。

 

在程序运行的时候,特别是程序里面发生异常的时候,如果你使用windbg.execdb.exe等命令行调试器的时候,或者如果你留心Visual Studio的输出( output)窗口里的文本输出的时候,你将会看到类似下面的输出:

First-chance exception at 0x004116a9 in 异常处理.exe: 0xC0000005: Access violation writing location 0x00000000.

……

First-chance exception at 0x7c812aeb in 异常处理.exe: Microsoft C++ exception: char at memory location 0x0012fd88..

 

异常机制简介

CPU运行到一些非法的指令,例如除零错误,访问内存页失败等指令,CPU会生成一个硬件异常,不同的异常有固定的异常代码作为标识符,异常产生以后CPU暂时不能继续执行后续的指令因为后续的指令有可能也是无效的。当然不能让整个计算机系统就这么当掉,因此CPU内置了一个异常处理表这个异常处理表只有运行在内核模式的代码才能访问,操作系统在启动的时候初始化这个异常处理表,为每一个异常注册一个异常处理程序,因此这个表看起来就像:

0xC0000005

AccessViolationExceptionHandler()

……

0x80000003

LaunchOrNotifyDebugger()

 

CPU产生异常以后,根据异常代码在异常处理表里面查找对应的异常处理程序,通过调用异常处理程序执行了合适的处理之后再继续执行其他的代码。

 

处理CPU自定义的硬件异常以外,异常处理表里面还有一些空的表项,这样操作系统可以通过添加自定义的异常代码和相应的异常处理程序实现软件异常。在Windows操作系统中,程序可以通过调用Win32RaiseException函数来触发软件异常。因此实际上C++.NET里面的异常都是通过调用RaiseException来实现的,然而异常处理表不是很大,只能保存不超过256个异常处理程序信息,因此Windows只定义了几个通用的异常码来表示C++ .NET异常。

 

0xE06D7363

表示C++ 异常

0xE0434f4D

表示CLR(或者说.NET)异常

 

对于CPU来说,硬件异常、软件异常、C++ 异常和.NET异常的触发和处理的方式都是一样的,这种方式在Windows中叫做结构化异常处理(SEH)。

异常处理

1.         当程序里面有一个异常发生以后;

2.         SEH首先通过回溯堆栈内容,查找程序内部是否有一个异常处理块(就是我们通常知道的catch 块);如果找到一个异常处理块可以处理这个异常,那么Windows将异常处理块所在的函数下面的堆栈释放,并且执行这个异常处理块里面的代码。

3.         如果Windows没有发现任何一个异常处理块处理掉这个异常的话,也就是到程序入口(main)函数也没有找到一个合适的异常处理块的话,Windows会使用它自带的异常处理块处理这个异常;

4.         Windows自带的异常块会检查你的程序是否附加了一个调试器,如果是的话,Windows中断程序并将控制权交给调试器。

5.         如果没有调试器附加到你的程序上,Windows启动注册在注册表里面的默认验尸调试器,一般情况下,Windows的默认调试器是Dr Watson,这个调试器的工作就是弹出著名的“调试还是终止,这个问题”对话框(或者是“是否将错误报告发送给微软?”对话框):

 

 

在上面的处理步骤中,第一步通过RaiseException触发异常的时候,Windows会先检查你的程序是否正在被调试,如果有一个调试正附加在你的程序上,Windows向调试发送一个调试消息,通知调试器程序里面有一个异常发生,询问调试器是否要中断程序执,默认情况下调试器会忽略这条消息因为我们纯洁的调试器总是相信程序员都能写出优良的代码出来。这个步骤在调试术语中叫做第一次机会(First Chance第一次在异常发生的现场观察触发异常的环境。

 

而第3步以后,操作系统自己来处理这个异常的时候,在调试术语中叫做第二次机会(Second Chance,由于这个时候程序实际上已经挂了(不会有任何生命活动了)不是病危,所以这个时候即使你将调试器附加上去来检查触发异常的环境时,就像法医在检查人非正常死亡的原因一样验尸调试。

设置调试器不要忽略first chance异常

程序开发的时候,我们当然是希望在程序病危(first chance异常)的时候检查代码错误(Bug)的原因啦,因此如果程序发生一些莫名其妙的Bug时,在调试程序的时候,最好通知调试器不要忽略first chance异常。因为如果异常被程序内部的代码catch掉的话,很有可能会导致我们忽略重要的线索。

 

Windbg里面,使用下面的命令来通知调试器不要忽略first chance异常:

sxe 异常代码

注意sxe后面有一个空格,例如在调试.NET程序的时候,可以使用命令sxe 0xE0434f4D来使调试器在catch块执行之前中断程序的执行。

可以使用命令

sxd 异常代码

来启用忽略first chance异常的功能。

 

Visual Studio里面,请参考我的另外一篇文章学习如何设置:CLR Debugger – 在程序抛出异常的时候中断程序的执行

如何知道调试器正在忽略first chance异常?

通过查看调试器里面的输出信息,你可以了解调试器是否启用了在first chance异常触发时中断程序执行的功能。

 

Visual Studio里面,可以查看“输出”窗口里面的文本输出:

这个输出就说明调试器忽略了first chance异常

 

First-chance exception at 0x004116a9 in 异常处理.exe: 0xC0000005: Access violation writing location 0x00000000.

……

First-chance exception at 0x7c812aeb in 异常处理.exe: Microsoft C++ exception: char at memory location 0x0012fd88..

 

Windbg里面,类似下面的输出表示调试器忽略了first chance异常:

0:000> g

(f08.f38): Access violation – code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

(f08.f38): C++ EH exception – code e06d7363 (first chance)

附录

你可以使用下面的代码去尝试一下上面讲解的概念:

// 异常处理.cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

#include <iostream>

#include <windows.h>

#include <excpt.h>

 

using namespace std;

 

int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)

{

    if ( code == EXCEPTION_ACCESS_VIOLATION )

        return EXCEPTION_EXECUTE_HANDLER;

    else

        return EXCEPTION_CONTINUE_SEARCH;

}

 

void CppTestFunction()

{

    try

    {

        throw "Test Cpp Exception";

    }

    catch ( char * )

    {

        cout << "char * exception caught" << endl;

    }

}

 

int _tmain(int argc, _TCHAR* argv[])

{

    int *p = NULL;

 

    __try

    {

        *p = 1;

    }

    __except ( ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))

    {

        cout << "ACCESS VIOLATION CAUGHT" << endl;

    }

 

    CppTestFunction();

 

    return 0;

}

[IIS]微軟的居家除蟲包--Debug Diagnostics Tool

mikel阅读(1360)

转载:http://ste0623.spaces.live.com/blog/cns!15D2D25FF6287C7A!498.entry

寫微軟的程式寫久了,最常見到但也最讓人無力的問題就是Server掛掉
可能是Memory不足,也可能是CPU 100%被佔據,而你完全不知問題在哪裡,只能乖乖重開機
重開機變成了Windows Server 的例行公事

或許有人知道Memory Dump這種東西,只有一群神一般的人才看得懂
對一般人而言想從Memory Dump看出什麼端倪簡直就是不可能的任務

但是,情況現在改觀了
微軟一向最厲害的就是,把艱難的東西用很容易上手的外裝包裝起來
也許功能因此減少了,但是上手卻容易了
最近就玩到這樣一個東西–Debug Diagnostics Tool
Debug IIS hang 簡直是容易到讓人無法相信

這東西要裝在你的IIS Server上,不過個人工作的電腦通常也要裝,因為dump完大概下一件事就是趕快重開機
讓production server趕快回復運作,哪裡還有那種美國時間讓你在上面跑分析
而且分析過程中要上網download symbol file,有時候production server是不對外的

裝完以後就像這樣,放在程式集裡面

執行起來的畫面像這樣,當你的IIS掛掉時,先不要急著重開
1.趕快先把犯罪的證據保留下來,從Tools->Create IIS Hang Dump將IIS相關的process都dump下來
2.然後再用最下面的Add Data Files將dump的結果讀進來
3.在上面的ListBox選擇 Crach/Hang Analyzers
4.點選下面的 Start Analysis

就這樣,夠簡單了
那分析結果如何解讀? Watch this

直接告訴你哪一個thread block住後面的程式,那麼thread 19是什麼?點一下超連結

直接告訴你這是哪支ASP,連第幾行都告訴你了  

那.NET程式可不可以?
我寫了一個無窮回圈的webservice

dump出來的report像這樣
 
  

這裡我還沒有研究透徹,因為只看到一堆循環不已的call
但是把同樣的dump用另一個工具 WinDbg 去讀,可以得到這樣的結果,連method都列出來給你

但是這個工具超級難用,要對memory dump以及很多系統面的東西有些了解才能運用自如(廢話,我就是看不懂memory dump咩)

Debug Diagnostics Tool 適合剛開始初學使用,我其實還是只用了很小一部分的功能,他還可以自訂rule,當crach/hang發生時自動幫你dump,更可以直接 attach 到某個process去debug
WinDbg不是給普通人用的,看看我列出來的連結就知道,給寫driver或玩系統核心的人用的….

這裡還有一些Debug Diagnostics Tool的教學,很清楚

這裡則有許多WinDbg的翻譯文章

先寫這些,因為用的真的還不多,但是因為太好用了,還是先把知道的寫出來

转载:

用WinDbg分析Debug Diagnostic Tool生成的Userdump文件

1、下载WinDbg(Debugging Tools for Windows):http://www.microsoft.com/whdc/devtools/debugging/default.mspx

2、安装WinDbg

3、运行WinDbg

4、配置Symbol文件路径: File>Symbol File Path,输入:SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols

5、打开Userdump文件:File>Open Crash Dump

6、执行命令:

  1)kbn

  2).loadby sos mscorwks

3)!clrstack

[IIS]为什么应用程序池总是崩溃(Crash)

mikel阅读(1052)

转载:http://blog.csai.cn/user1/17029/archives/2006/6810.html 
   过了一个既很有意义又很郁闷的五一节,有意义的是为了解决博客园Blog程序中一个奇怪的问题劳动了7天,郁闷的是到现在问题还没解决。
     这个问题的特征可以用一个字形容:怪。
     这个问题的主题:Blog应用程序引起的IIS 6应用程序池崩溃。
     问题的主要现象:
     当把新版的Blog程序投入到正式运行环境中后,一开始运行正常,过几分钟后,打开页面速度就变得很慢,浏览器一直处于请求状态(浏览器右上角的图标一直 在忙碌),却得不到服务器的正常响应,我的理解就是IIS虽然接受了请求,但应用程序池中的程序却不能对请求作出响应,从而让浏览器在苦苦等待。这 时,CPU占用却很少,系统事件日志中会出现这样的警告:    

A process serving application pool 'AppPool_CNBlogs_New' failed to respond to a ping. The process id was '3844'.

我把这样的现象描述为:应用程序池崩溃。
当 应用程序池崩溃时,运行于内核模式的HTTP.SYS会建立一个新的应用程序池进程w3wp.exe 处理新的请求,并回收旧的应用程序池,可新的应用程序池进程运行一会儿又崩溃,IIS又建立新的应用程序池进程,这样反反复复,网站处于一种很不稳定的运 行状态。当IIS回收旧的应用程序池时,系统事件日志中还会出现这样的警告:

A process serving application pool 'AppPool_CNBlogs_New' exceeded time limits during shut down. The process id was '2380'. 

这个警告是通配符映射应用程序存在的通病,可能是通配符映射这样的方式让IIS无法对应用程序池占用的所有资源进行正常回收。
     对于这个问题,大家都知道肯定是程序中的Bug,而关键问题是找出Bug所在,而我七天的努力却一无所获。同样的程序在本机和服务器上测试都很正常,可是 一切换到正式运行环境就出问题。新版本中代码改动不少,但我把主要的改动恢复了也不能解决问题,几天来在代码苦苦寻找Bug的线索也没有收获,也许是很小 的代码问题引起的,但我就是找不到。如果没有一定的线索,即使将所有代码检查一遍,也不一定能找到Bug所在。
     今天,我用Debug Diagnostics Tool 1.0 捕获应用程序池崩溃时的一些信息,Debug Diagnostics Tool 1.0 会在应用程序池崩溃时生成dmp文件并能对dmp文件进行分析,分析的结果是:

the assembly instruction at kernel32!RaiseException+53 in C:\WINDOWS\SysWOW64\kernel32.dll has caused an unknown exception (0xe0434f4d) on thread 56
This exception originated from mscorwks!RaiseTheExceptionInternalOnly+226. 

想看详细结果请下载结果文件(mht文件)。
从分析结果可以看出在程序运行时产生了未处理异常,而该异常让应用程序池崩溃,我接下来的任务是找出这些未处理异常,将参考文章:
1、http://support.microsoft.com/?id=911816
2、http://blogs.msdn.com/tess/archive/2006/04/27/584927.aspx
今天晚上的任务就是找出未处理异常,就写到这,继续去处理这个问题。
希望有经验的朋友能够提供一些参考意见。
一些参考文章:
HOWTO: Understand and Diagnose an Application Pool Crash 
ASP.NET 2.0 Crash case study: Unhandled exceptions
Unhandled exceptions cause ASP.NET-based applications to unexpectedly quit in the .NET Framework 2.0

[C#]简单研究一下asp.net上传文件时的内存占用问题

mikel阅读(803)

转载:http://www.cnblogs.com/william_fire/articles/31324.html

最近看见有人在研究ASP.NET的上传组件,如何上传大文件的问题,于是简单地研究了一下,发现了一些东西,总结如下,希望大家能够提出一个较好的解决方案。

据msdn所言,在Web.Config中,默认可以上传的文件大小为8M,如果需要上传更大的文件,则需要手动指 定: 这里的示例是最大100M的,经过测试,100M以下的文件还是可以正常上传的(占用内存必须在服务器可用内存60%以下)在使用.NET中的文件上传的 功能时,实际上使用的是Html控件,并不是Web控件,这就意味着,HtmlInputFile控件不是明显地支持服务端的Event的,所以,提交 时,应该使用Html控件的Button进行Submit。

经测试,可以发现,当不断地进行上传文件的时候,内存的消耗是不断地累积的,也就是说,当你上传了一个20M的文件 后,再上传20M时,缓存中内存消耗会达到40M左右。 在MSDN文档中的说明里面指出,当文件上传到服务器后,是存储在缓存中的,在没有把缓存中的数据存储成文件时,是不会清空缓存的。

其实,这句话有一定的误导性,因为在HtmlInputFile控件中,PostedFile属性中,提供了一个 SaveAs方法成员,给许多人一种错觉就是,当进行了HtmlInpuFile的SaveAs后,缓存会被清空。实际上不是这样,首先,查看一下 HtmlInputFile中的PostedFile属性的代码,如下:

[WebCategory("Default"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue("")]
public HttpPostedFile PostedFile
{
 get { return this.Context.Request.Files[this.RenderedNameAttribute]; }
}
可以看出,实际上是一个HttpPostedFile类实例,那么要明白它的工作原理,肯定就要追到HttpPostedFile类去看看。

好了,我们再看看,这个HttpPostFile中提供的SaveAs()的代码:
public void SaveAs(string filename)
{
 HttpRuntimeSection section1;
 if (!Path.IsPathRooted(filename))
  {
  section1 = RuntimeConfig.GetConfig().HttpRuntime;
  if (section1.RequireRootedSaveAsPath)
  {
    throw new HttpException(HttpRuntime.FormatResourceString("SaveAs_requires_rooted_path", filename));
  }
  }
  FileStream stream1 = new FileStream(filename, FileMode.Create);
  try
  { 
  this._stream.WriteTo(stream1);
  stream1.Flush();
  return;
  }
  finally
  {
  stream1.Close();
  }
}

注意,用标记的那一句,观看整个代码,可以发现的是:实际上,在SavaAs方法中,处理的流是在此方法中新建的一 个FileStream的实例,这里只有stream1.Flush(),而this._stream所指向的实例实际上还存在于缓存中,它才是真正占用 了大量内存空间的罪人。

好了,再看看HttpPostedFile的构造函数,就可以知道this._stream是从哪里来的了.
internal HttpPostedFile(string filename, string contentType, HttpInputStream stream)
 {
  this._filename = filename;
  this._contentType = contentType;
  this._stream = stream;
 }
嗯?原来如此,这里的流对象原来也只是一个引用罢了,真正的占用内存的杀手在哪儿?看样子,只要知道了是谁创建了HttpPostedFile这个类的实例,谁就是罪魁祸首。

不过,这里,我们要暂放一步,目前我们基本知道了实际上进行SaveAs方法后,缓存中的数据并没有被清除,相反 的,它还好好地留在内存中。 我们的目标是要解决这个问题,正好,在HtmlInputFile中也给出了所创建的实例的引用,所以,现在,针对HttpInputStream流来进 行一些处理。

保险起见,我们先看看HttpInputStream的内容吧,再作定论吧。

public override void Flush() { }
@#%*&@,Flush方法居然是这样写的,嗯,以后要注意了!

public override void Close() { this.Uninit(); }
这是Close方法,里面调用了Uninit(),所以,我们再看看Uninit.

protected void Uninit() { this._data = null; this._offset = 0; this._length = 0; this._pos = 0; }
Mmmm,虽然感觉有点不爽,但起码使用这个方法时,这个方法能够保证它会把缓存对象清空。

最后,写出的代码类似下面的形式:
private void btnUpLoad_ServerClick(object sender, System.EventArgs e)

 try
 {
   string strFileName = new FileInfo(imageUpload.PostedFile.FileName).Name;
   UploadFile.PostedFile.SaveAs("C:\\"+strFileName);
   Response.Write("上传成功!");
 }
 catch
 {
  Response.Write("上传失败!");
 }
 finally
 { 
   HttpInputStream upStream = imageUpload.PostedFile.InputStream;
   upStream.Close();
  }
}
经过测试,发现这样也不是一个理想的方案,还必须进一步补充才行。
过程如下:
在本机上,浏览FileUpload.aspx,此时,w3wp进程占用内存空间为28,948K
当上传一个12.7M的文件后,据返回的信息表示,上传的文件大小为133307341,内存占用变化为49,312K。
再上传一个一个371K的文件,据返回的信息表示,上传的文件上小为3799041,内存占用此时为31,540K
似乎达到了我们的要求,但这里有一个问题就是,当你上传一个大文件后,没有再上传一个小文件的话,那么这个大文件将会在内存里一直占用空间,不知,是否还有更好的办法去解决,现在快1点了,睡觉,以后再说。

[AR]很酷的AR演示

mikel阅读(871)

转载:http://www.7yue.com/post/377.html
记得以前我有几篇文章介绍过关于AR的FLARKit和PV3D结合的AR演示Demo,后来又提到过GE,尼桑,卢卡斯星战网站等等都纷纷用到AR来应用于自己的产品宣传和应用演示。
现在再给大家2个非常酷的AR视频,这次则是Nvidia等公司出场。第一个视频在Youtube上已经被观看了将近50万次。
http://www.youtube.com/watch?v=cNu4CluFOcw
第二个AR的演示则是来自乐高玩具。
http://www.flickr.com/photos/danieldura/3952886349/
更新部分:
第三个AR演示来自梅赛德斯奔驰
http://www.virtueleopendeur.be/

此次Adobe MAX 2009大会也会有引人入胜的AR演讲环节,看来AR真的是火了。

[C#].NET深入学习笔记(3):垃圾回收与内存管理

mikel阅读(832)

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个《WCF分布式开发必备知识》系列后的一次休息吧。以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会问相关的问题。那么你是否也遇到这样的问题呢?比如你清楚.Net的垃圾回收机制吗?你能简述一下GC的工作原理吗?怎么样才能有效的管理内存呢?Using语句体内实例化的对象有什么作用?等等相关问题。下面我们就来详细讨论一下。相信你看完以后也可以面试别人。

     本节的组织如下,1..Net的类型和内存分配2.GC垃圾收集器的工作原理3.什么是非托管资源4.如何有效释放对象资源。总结.现在开始我们本节的学习。

    1..Net的类型和内存分配

     Net中的所有类型都是(直接或间接)从System.Object类型派生的。

    CTS中的类型被分成两大类——引用类型(reference type,又叫托管类型[managed type]),分配在内存堆上,值类型(value type)。值类型分配在堆栈上。如图

   

     值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值类型变量在推出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。

     引用类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当使用new创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存,如图

  

    2.GC垃圾收集器的工作原理

      上图中,当dataSet使用过期以后,我们不显示销毁对象,堆上的对象还继续存在,等待GC的 回收。

垃圾收集器通过分代支持对象的年龄化是推荐的但不是必需的。一代在内存里是一个具有相对年龄的对象的单位。对象的

代号或年龄标识对象属于那个分代。在应用程序的生命周期里,越近创建的对象属于越新的代,并且比早创建的对象具有

较低的分代号。最近分代里的对象代号是0.

      在new对象时,要先搜索空闲链表,找到最适合内存块,分配,调整内存块链表,合并碎片。new操作几乎可以在O(1)的时间完成,把堆顶指针加1。工作原理是: 当托管堆上剩余空间不足,或者Generator 0 的空间已满的时候GC运行,开始回收内存。垃圾回收的开始,GC对堆内存的压缩调整,对象集中到顶部。GC在扫描垃圾的时候会占用一定的CPU时间片的,最初的GC算法真的是扫描整个堆,效率低。现在的GC把堆中的对象分成3代,最进入堆的是第0代(generation 0), 其次是generation 1, generation2. 第一次GC只扫描第0代。如果回收的空间足够当前使用就不必扫描其它generation的对象。所以,GC创建对象的效率比C++高效,不需要扫描全部堆空间。它通过扫描策略,再加上内存管理策略带来的性能提升,足以补偿GC所占用的CPU时间。

    3.什么是非托管资源

  常见的非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它知道如何清理这些资源。好在.net Framework提供的Finalize()方法,它允许在垃圾回收器回收该类资源前,适当的清理非托管资源。这里列举几种常见的非托管资源:画笔、流对象、组件对象等等资源(Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,ApplicationContext,Brush,

Component,ComponentDesigner,Container,Context,Cursor,FileStream,

Font,Icon,Image,Matrix,Timer,Tooltip)。(参考MSDN)

    4.如何有效释放非托管资源。

     GC无法管理非托管资源,那么如何释放非托管资源呢?.Net提供了两种方式:

(1)析构函数:垃圾收集器回收非托管对象的资源时,会调用对象的终结方法Finalize(),进行资源的清理工作,但是由于GC工作规则的限制,GC调用对象的Finalize方法,第一次不会释放资源,第二次调用之后才删除对象。

(2)继承IDisposable接口,实现Dispose()方法,IDisposable接口定义了一个模式(具有语言级的支持),为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾收集器相关的问题。

   为了更好的理解垃圾回收机制,我特地写了部分代码,里面添加了详细的注释。定义单个类FrankClassWithDispose(继承接口IDisposableFrankClassNoFinalize(没终结器)FrankClassWithDestructor(定义了析构函数)。

具体代码如下:


 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Data;
 5using System.Data.Odbc;
 6using System.Drawing;
 7//Coded By Frank Xu Lei 18/2/2009
 8//Study the .NET Memory Management
 9//Garbage Collector 垃圾收集器。可以根据策略在需要的时候回收托管资源,
10//但是GC不知道如何管理非托管资源。如网络连接、数据库连接、画笔、组件等
11//两个机制来解决非托管资源的释放问题。析构函数、IDispose接口
12//COM引用计数
13//C++手动管理,New Delete
14//VB自动管理
15namespace MemoryManagement
16{
17    //继承接口IDisposable,实现Dispose方法,可以释放FrankClassDispose的实例资源
18    public class FrankClassWithDispose : IDisposable
19    {
20        private OdbcConnection _odbcConnection = null;
21        
22        //构造函数
23        public FrankClassWithDispose()
24        {
25            if (_odbcConnection == null)
26                _odbcConnection = new OdbcConnection();
27            Console.WriteLine("FrankClassWithDispose has been created ");
28        }

29        //测试方法
30        public void DoSomething()
31        {
32
33            ////code here to do something
34            return ;
35        }

36        //实现Dispose,释放本类使用的资源
37        public void Dispose()
38        {
39            if (_odbcConnection != null)
40                _odbcConnection.Dispose();
41            Console.WriteLine("FrankClassWithDispose has been disposed");
42        }

43    }

44    //没有实现Finalize,等着GC回收FrankClassFinalize的实例资源,GC运行时候直接回收
45    public class FrankClassNoFinalize
46    {
47        private OdbcConnection _odbcConnection = null;
48        //构造函数
49        public FrankClassNoFinalize()
50        {
51            if (_odbcConnection == null)
52                _odbcConnection = new OdbcConnection();
53            Console.WriteLine("FrankClassNoFinalize  has been created");
54        }

55        //测试方法
56        public void DoSomething()
57        {
58
59            //GC.Collect();
60            ////code here to do something
61            return ;
62        }

63    }

64    //实现析构函数,编译为Finalize方法,调用对象的析构函数
65    //GC运行时,两次调用,第一次没释放资源,第二次才释放
66    //FrankClassDestructor的实例资源
67    //CLR使用独立的线程来执行对象的Finalize方法,频繁调用会使性能下降
68    public class FrankClassWithDestructor
69    {
70        private OdbcConnection _odbcConnection = null;
71        //构造函数
72        public FrankClassWithDestructor()
73        {
74            if (_odbcConnection == null)
75                _odbcConnection = new OdbcConnection();
76            Console.WriteLine("FrankClassWithDestructor  has been created");
77        }

78        //测试方法
79        public void DoSomething()
80        {
81            ////code here to do something
82
83            return ;
84        }

85        //析构函数,释放未托管资源
86        ~FrankClassWithDestructor()
87        {
88            if (_odbcConnection != null)
89                _odbcConnection.Dispose();
90            Console.WriteLine("FrankClassWithDestructor  has been disposed");
91        }

92    }

93}

94

其中使用了非托管的对象OdbcConnection的实例。建立的客户端进行了简单的测试。客户端代码如下:

 

 

Code

 有些时候资源必须在特定时间释放,类可以实现执行资源管理和清除任务方法IDisposable.Dispose的接口IDisposable。
如果调用者需要调用Dispose方法清理对象,类作为契约的一部分必须实现Dispose方法。垃圾收集器默认情况下不会调用
Dispose方法;然而,实现Dispose方法可以调用GC里的方法去规范垃圾收器的终结行为。

值得一提的是:调用Dispose()方法,主动释放资源,灵活,可以使用Using语句创建非托管对象,方法执行结束前,会调用
Dispose()方法释放资源,
这两端代码的效果是一样的,可以查看编译后IL。

Code

Using 语句有同样的效果,来实现非托管对象资源的释放。这点在面试中也会经常遇到,Using关键字的用法有哪几种等等类似的问题。基本理想的答案都是除了引用命名空间,和命名空间设置别名外,就是这个用法实现如try finally块一样作用的对非托管对象资源的回收。只是一种简便的写法。

     当你用Dispose方法释放未托管对象的时候,应该调用GC.SuppressFinalize。如果对象正在终结队列(finalization queue),GC.SuppressFinalize会阻止GC调用Finalize方法。因为Finalize方法的调用会牺牲部分性能。如果你的Dispose方法已经对委托管资源作了清理,就没必要让GC再调用对象的Finalize方法(MSDN)。附上MSDN的代码,大家可以参考.

Code

 

    总结:看了本文以后,不知对你是否有所帮助,如果你理解了.net垃圾回收的机制和GC的工作原理,以及包含如何管理非托管资源,你就会成为一个内存管理的高手。如果面试官问道这个问题,你就可以详细阐述你对这类问题的理解和看法。希望这篇文章能对你的工作和学习带来帮助~

DemoCodes/Files/frank_xl/MemoryManagement2008.zip

 

 


 

 

老徐的博客

【作者】:Frank Xu Lei

【地址】:http://www.cnblogs.com/frank_xl/archive/2009/02/19/1393566.html

[IIS]w3wp.exe 频繁重启 Faulting application w3wp.exe

mikel阅读(600)

转载:http://blog.sina.com.cn/s/blog_545edcb00100at2k.html
w3wp.exe 频繁重启 (Faulting application w3wp.exe) Faulting application w3wp.exe, version 6.0.3790.3959, stamp 45d6968e, faulting module kernel32.dll, version 5.2.3790.3959, stamp 45d742c2, Debug? 0, fault address 0x0000bee7. 中文搜索没有找到解决办法。英文搜索也没有找到解决办法。最后自己还是只能靠自己。原因在于,IIS的应用程序池主动回收w3wp.exe进程,就会使其 崩掉。这应该是微软的一个BUG。解决办法非常简单,打开应用程序池的属性页,在“回收”选项卡把所有的复选框去掉,让.NET的垃圾回收来管理内存而不 要让IIS来插手。