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

mikel阅读(689)

转载: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阅读(542)

转载: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阅读(1086)

转载: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阅读(807)

转载: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阅读(538)

转载: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阅读(564)

转载: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阅读(555)

    今天抽空来讨论一下.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阅读(431)

转载: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来插手。

[Memcached]Windows下的.NET+ Memcached安装

mikel阅读(540)

转载请标明出处: http://www.yaosansi.com

原文:http://www.yaosansi.com/post/1396.html

 

Memcached官方:http://danga.com/memcached/

关于Memcached的介绍请参考:Memcached深度分析

下载Windows的Server端

下载地址:http://code.jellycan.com/memcached/

安装Memcache Server(也可以不安装直接启动)

1. 下载memcached的windows稳定版,解压放某个盘下面,比如在c:\memcached
2. 在CMD下输入 "c:\memcached\memcached.exe -d install" 安装.
3. 再输入:"c:\memcached\memcached.exe -d start" 启动。NOTE: 以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。

如果下载的是二进制的版本,直接运行就可以了,可以加上参数来加以设置。

常用设置:
-p <num>          监听的端口
-l <ip_addr>      连接的IP地址, 默认是本机
-d start          启动memcached服务
-d restart        重起memcached服务
-d stop|shutdown  关闭正在运行的memcached服务
-d install        安装memcached服务
-d uninstall      卸载memcached服务
-u <username>     以<username>的身份运行 (仅在以root运行的时候有效)
-m <num>          最大内存使用,单位MB。默认64MB
-M                内存耗尽时返回错误,而不是删除项
-c <num>          最大同时连接数,默认是1024
-f <factor>       块大小增长因子,默认是1.25
-n <bytes>        最小分配空间,key+value+flags默认是48
-h                显示帮助

然后就可以用.net 的memcached客户端来试一下了。

C# 下可用的API(每个客户端API中都有详细的说明和注释)

https://sourceforge.net/projects/memcacheddotnet/
http://www.codeplex.com/EnyimMemcached/ – Client developed in .NET 2.0 keeping performance and extensibility in

mind. (Supports consistent hashing.)
http://code.google.com/p/beitmemcached/ – Client developed by BeIT with many new features.