zoukankan      html  css  js  c++  java
  • .NET运用AJAX 总结及其实例

    .NET运用AJAX 总结及其实例

    1、AJAX简介

         (1、没有AJAX会怎么样?普通的ASP.Net每次执行服务端方法的时候都要刷新当前页面,比如实现显示服务器的时间。每次都要刷新页面的坏处:页面刷新打断用户操作、速度慢、增加服务器的流量压力。如果没有AJAX,在youku看视频的过程中如果点击了“顶、踩”、评论、评论翻页,页面就会刷新,视频就会被打断。试想一个效果:在YOUKU网看视频,然后看到一个顶踩的功能,看没有ajax会打断视频,然后将按钮用UpdatePanel包起来就不会打断视频了。用HttpWatch看没有AJAX的时候服务器返回的是整个页面,有了AJAX服务器只返回几个按钮的内容。

       (2、AJAX(AsynchronousJavaScript and XML,异步JavaScript和XML)是一种进行页面局部异步刷新的技术。用AJAX向服务器发送请求和获得服务器返回的数据并且更新到界面中,不是整个页面刷新,而是在HTML页面中使用JavaScript创建XMLHTTPRequest对象来向服务器发出请求以及获得返回的数据,就像JavaScript版的WebClient一样,在页面中XMLHTTPRequest来发出Http请求和获得服务器的返回数据,这样页面就不会刷新了。XMLHTTPRequest是AJAX的核心对象
     
    2、 XMLHTTPRequest

        (1、开发一个AJAX功能需要开发服务端和客户端两块程序。以一个显示服务端时间为例。首先开发一个GetDate1.ashx,输出当前时间。在HTML页面中放一个按 钮,在按钮的onclick中创建XMLHTTP向GetDate1.ashx发送请求,获得返回的数据并且显示到界面上。下面的代码非常重要,是精华来着,必背:
    javascript代码
     1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <title>AJAX01</title>
     5     <script type="text/javascript">
     6         function btnClick() {
     7             //alert(1);
     8             // 1 创建XMLHTTP对象,相当于WebClient
     9             var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 
    10             
    11             if (!xmlhttp) {
    12                 alert("创建xmlhttp对象异常");
    13                 return;
    14             }
    15             
    16             // 2 “准备”01AJAX.ashx发出Post请求。这里还没有发出请求
    17             //XMLHTTP默认(也推荐)不是同步请求的,也就是open方法并不像WebClient的DownloadString
    18             //那样把服务器返回的数据拿到才返回,
    19             //是异步的,因此需要监听onreadystatechange事件
    20 
    21 
    22             xmlhttp.open("POST", "01AJAX.ashx?id=" + encodeURI('AJAX服务器') + "&ts=" + new Date(), false);
    23             
    24             xmlhttp.onreadystatechange = function () {
    25                 if (xmlhttp.readyState == 4) {//readyState == 4 表示服务器返回数据了
    26                     if (xmlhttp.status == 200) {//如果状态码为200则是成功
    27                         //接收服务器的返回数据,没有用send的返回值,而是在onreadystatechange事件里来接收
    28                         document.getElementById("txtTime").value = xmlhttp.responseText; //responseText属性为服务器返回的文本
    29                     }
    30                     else {
    31                         alert("AJAX服务器返回错误!");
    32                     }
    33                 }
    34             }
    35             //不要以为if(xmlhttp.readyState == 4) 在send之前执行!!!!
    36             //if (xmlhttp.readyState == 4)只有在服务器返回值以后才会执行,而!!send之后过一会儿服务器才会返回数据
    37             xmlhttp.send(); //这时才开始发送请求
    38         }
    39     </script>
    40 </head>
    41 <body>
    42     <input type="text" id="txtTime" />
    43     <input type="button" id="btn" value="button" onclick="btnClick()" />
    44 </body>
    45 </html>
    ashx代码
    1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 
     6 namespace AJAX
     7 {
     8     /// <summary>
     9     /// _01AJAx 的摘要说明
    10     /// </summary>
    11     public class _01AJAx : IHttpHandler
    12     {
    13 
    14         public void ProcessRequest(HttpContext context)
    15         {
    16             context.Response.ContentType = "text/plain";
    17             string id = context.Request["id"];
    18             context.Response.Write(DateTime.Now.ToString()+"---"+id);
    19         }
    20 
    21         public bool IsReusable
    22         {
    23             get
    24             {
    25                 return false;
    26             }
    27         }
    28     }
    29 }
       (2、不使用UpdatePanel、JQuery等AJAX库编写一个AJAX程序。 也可以在xmlhttp.open中向服务器传递参数:
    xmlhttp.open("POST","GetDate1.ashx?id=1&name="+"encodeURL('中国')", false),如果传递给服务器的请求里有中文,则需要使用Javascript函数encodeURI来进行URL编码。
     
              (3、发出请求后不等服务器返回数据,就继续向下执行,所以不会阻塞,界面就不卡了,这就是AJAX中“A”的含义“异步”。只有在服务器返回值以后才会执
           行,而!!send之后过一会儿服务器才会返回数据。
              (4、 xmlhttp.open("GET","GetDate1.ashx?id=1&name="+"encodeURL('中国')", false),如果这样单纯滴传两个静态参数的话,浏览器可能会保持一
           种提取缓存的状态,所以最好传一个动态参数,随意一个参数。这是一个AJAX缓冲存在的问题。如果用POST的方式,就不会发生缓冲的问题。
     案例1:无刷新异步操作-->汇率转换
    html代码
    1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <title>02 huilv question</title>
     5     <script src="js/jquery-1.4.2.js" type="text/javascript"></script>
     6     <script src="js/jquery-1.4.2-vsdoc.js" type="text/javascript"></script>
     7     <script type="text/javascript">
     8         function btnOnclick() {
     9             var moneyType = $("#selectedID").val();
    10             var account = $("#myaccount").val();
    11             //alert(account);
    12             //alert(moneyType);
    13             var xmlhttp =new ActiveXObject("Microsoft.XMLHTTP");
    14             if (!xmlhttp) {
    15                 alert("error from create xmlhttp!");
    16                 return;
    17             }
    18             xmlhttp.open("POST", "02" + encodeURI('汇率问题') + ".ashx?moneyType=" + moneyType + "&account=" + account + "&ts=" + new Date(), false);
    19             xmlhttp.onreadystatechange = function () {
    20                 if (xmlhttp.readyState == 4) {
    21                     alert(xmlhttp.responseText);
    22                     if (xmlhttp.status == 200) {
    23                         alert(xmlhttp.responseText);
    24                         //$("#result").text = xmlhttp.responseText;
    25                         $("#result").val(xmlhttp.responseText);
    26                     }
    27                 }
    28             }
    29             xmlhttp.send();
    30         }
    31 
    32     </script>
    33 </head>
    34 <body>
    35     <input id="myaccount" type="text" name="name" value="" />
    36     <select id="selectedID">
    37         <option value="1" selected="selected">dollar</option>
    38         <option value="2">Japan</option>
    39         <option value="3">Hongkong</option>
    40     </select>
    41     <input type="button" name="name" value="check" onclick="btnOnclick()" />
    42     <input type="text" name="name" value=" " id="result"/>
    43 </body>
    44 </html>
    复制代码
    ashx代码
    1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 
     6 namespace AJAX
     7 {
     8     /// <summary>
     9     /// _02汇率问题 的摘要说明
    10     /// </summary>
    11     public class _02汇率问题 : IHttpHandler
    12     {
    13 
    14         public void ProcessRequest(HttpContext context)
    15         {
    16             context.Response.ContentType = "text/plain";
    17             //context.Response.Write("Hello World");
    18             //get two accounts from html
    19             string moneyType = context.Request["moneyType"];
    20             int account = Convert.ToInt32(context.Request["account"]);
    21 
    22             if (moneyType == "1")//dollar
    23             {
    24                 context.Response.Write(account/7);
    25             }
    26             else if(moneyType=="2")//Japan
    27             {
    28                 context.Response.Write(account*10);
    29             }
    30             else//Hongkong
    31             {
    32                 context.Response.Write(account*10/9);
    33             }
    34         }
    35 
    36         public bool IsReusable
    37         {
    38             get
    39             {
    40                 return false;
    41             }
    42         }
    43     }
    44 }

    !!!遇到问题总结:
            xmlhttp.open("POST", "02" + encodeURI('汇率问题') + ".ashx?moneyType=" + moneyType + "&account=" + account + "&ts=" + new Date(), false);这句代码中,用到中文字符都要用encodeURl来转化字符类型,不仅仅是参数,页面名称亦如是。

           $("#result").val(xmlhttp.responseText);将xmlhttp获取得到的文本数据传给val()。

    3、JQuery AJAX

           (1、newActiveXObject("Microsoft.XMLHTTP")是IE中创建XMLHttpRequest对象的方法。非IE浏览器中创建方法是newXmlHttpRequest()。为了兼容不同的浏览 器需要编写很多代码,下面的兼容浏览器也不是很完整的:
      
    兼容不同浏览器的XMLhttpresquest对象

           (2、采用JQueryAJAX方式可以高效化解浏览器问题:JQuery中提供了简化ajax使用的方法。$.ajax()函数是JQuery中提供的ajax访问函数,

         $.post()是对$.ajax()的post方式提交ajax查询的封装,$.get()是对$.ajax()的get方式提交ajax查询的封装。推荐用post方式,因为post方式没有缓存的问题
    案例1:对前面的汇率问题进行修改简化

    JQuery改进汇率兑换问题

     4、练习

     练习1:JQuery实现Ajax 根据商品名称自动显示价格
    html代码
    ashx代码

    !!!遇到问题总结:

            发现错误,调试了半天,但是根本无法进入到应该处理的代码段进行调试。后来经过一番查找,得出原因!!!

            我是直接从之前的其他页面拷贝整个ashx 文件然后修改成到现在的文件,VS2010 没有自动帮我修改ashx文件所指向的类,必须手工进行修改。

              解决方法:右键点击该 ashx 文件,选择“查看标记”,在打开的编辑界面中,修改 Class 项,改为新项目所指向命名空间下的类名。

     练习2:无刷新评论帖子
        方法1:评论采用AJAX,但采用Repeater动态显示列表
    html代码
    ashx代码

       方法2:评论和列表均采用AJAX,完全静态操作

    html代码
    ashx代码

     总结:如果想要控制用户的评论,例如禁止用户输入粗话等不文明用语,可以在ashx文件中添加  if(Msg.Contains("粗话")){return;}

     5、Json
     
             (1、ajax传递复杂数据如果自己进行格式定义的话会经历组装、解析的过程。因此ajax中有一个事实上的数据传输标准json,json将复杂对像序列化为一个字符串,在浏览端再将字符串反序列化为javascript可以读取的对像,看一下json的格式,json被几乎所有语言支持

             (2、c#中将.net对像序列化为json字符串的方法: javascriptserializer().serialize(p),javascriptSerializer在System.web.extensions.dll中,是net3.x中新增的类,如果在.net2.0则需要用第三方的组件。

              (3、Jquery ajax得到的data是Json格式数据,用$.parseJSON(data)方法将json格式数据解析为javaScript对像。

     练习1:用Json实现类中数据的传递
    html代码
    ashx代码

    总结:JavaScriptSerializer要引用using System.Web.Script.Serialization;但是using System.Web.Script.Serialization;引用的前提是引用System.web.extensions.dll

     练习2:用Json实现无刷新评论列表分页

    ashx代码
    html代码
    分页的SQl语句

     练习3:用Json实现无刷新删除评论

    ashx代码
    aspx代码

    6、微软中的AJAX应用

        (1、ASP.NET中内置的简化AJAX开发的控件UPdatePanel

                ☆  放入ScriptManager,将要实现AJAX效果的控件放到UpdatePanel 中即可

                ☆ UpdatePanel远离揭秘,用httpWatch看
                        原理:
                        缺点:通讯量傻瓜化过大,浪费流量,适合企业内部用。Timer就是实现定时AJAX效果,但是数据量也很大
     
                ☆ 只需要把无刷新更新的部分放到UPdatePanel中
        (2、用WCF简化AJAX代码量,让javascript写C#代码
     
        (3、UpdateProgress显示正在加载
     
    7、WCF了解

    1.开发步骤:
         ①添加一个Web项目 在Web项目中新建”新建项”→”Web”→”启用了AJAX的WCF服务
         ②页面上拖放ScriptManager控件 Services属性中新增一项 Path属性设置为服务路径
         ③调用服务端方法时
                  Service1.DoWork(OnDoWorkSuccess, OnDoWorkFailed);Service1为服务类名 DoWork为方法名 OnDoWorkSuccess为成功时回调函数 OnDoWorkFailed为失败时回调函数 两个函数都有一个参数result 成功时参数作为返回值 失败时参数作为错误消息。服务器端还可以返回复杂对象 浏览器端可以直接从result读取复杂对象的字段值

     8、全局文件

         (1、"web" 全局应用程序类(注意文件名不要改)

    ①全局文件是对Web应用生命周期的一个事件响应的地方
    ②将Web应用启动时初始化的一些代码写到Application_Start中(如Log4Net等)
    ③Web应用关闭时Application_End调用
    ④Session启动时Session_Start调用
    ⑤Session结束(用户主动退出或者超时结束)时Session_End调用
    ⑥当一个用户请求来的时候Application_BeginRequest调用
    ⑦当应用程序中出现未捕获异常时Application_Error调用(通过HttpContext.Current.Server.GetLastError()获得异常信息 然后用Log4Net记录到日志中)

    练习:禁止盗链和IP地址禁止

    Global.asax

    9、URL重写

    1. SEO(Search Engine Optimization): 搜索引擎优化
    2. URL重写(URLRewrite 伪静态):搜索引擎优化也方便用户看
    3. Site:cnblogs.com  能看百度收录了多少网页

        !!!实现:当打开View-1.aspx、View-2.aspx重写,都是指向同一个页面

     原理: 在Global.asax的Application_BeginRequest中读取请求的URL并使用HttpContext.Current.Rewrite()进行重写
      Regex reg = new Regex(“.+View-(\d+).aspx”);
      var m1 = reg.Match(HttpContext.Current.Request.Url.AbsolutePath);
      if(macth.Success)
      {
        string id = match.Groups[1].Value;
        HttpContext.Current.RewitePath(“View.aspx?id=” + id);
      }

    项目从VS2010 升 VS2012 遇到的代表性问题及解决

     

    最近公司要把项目从使用VS2010 转成VS2012,开始以为是个很简单的事情,后来发现有点头疼。

    现在问题解决了,发现主要有以下3个方面的问题

    1. 第三方组件,对2012的支持的技术问题
    2. VS2012本身的bug
    3. 本身代码潜在的问题

    先解决第一个问题,主要是和第三方测试工具typemock有关,我的丹麦同事和typemock公司沟通了很多次和自己反复测试之后解决了问题

    解决方案

    上面提到的typemock小问题,也由以下方式解决了

    I suggest you decorate the test class with [Isolated] instead of using it on each method or better yet use Isolated on the entire assembly.

    In order to decorate assemblies with Isolated you should add 
    [assembly: Isolated()] to AssemblyInfo.cs which is the same as decorating each test method with Isolated.

    下面解决第二个问题,VS2012本身bug

    VS2010 LocalTestRun.restrunconfig 本来可以用目录的方式在,测试开始前拷贝一些文件到测试环境,如下

    VS2012中 我发现需要的xml文件都没有拷贝到测试目录,于是试了下 直接指定文件如下居然可以了,我晕倒,不过这个bug在update2中解决了,我有试过

     最后一个问题,看到直接奔溃的问题,说 agent process was stopped while the test was running, 如下

    开始看到这种错误直接没头绪,下意识就是网上一通子搜,相关网页很少而且每个人解决的方式也是不一样的。不过听说是可能是多线程的问题,因为这种错误一般是测试运行时后台起的多线程挂掉了。

    后来抱着试试看的心态,看了看Windows log,果然还找到一个相关错误。 然后直接对应到代码 呵呵 还真是线程问题。

    具体怎么解决就不赘述,因为解决方法可以有很多种,关键知道是后台线程测试时有抛异常挂掉就行了。

    最后看到全绿之后,我就开始写博客了 :)绿色令程序员有种莫名的骄傲,幸福和喜悦感~~

     
     
     
    标签: VS2010VS2012Unit Test

    .NET垃圾回收 问题、建议

    基础知识:CLR垃圾回收器采用代(generation)机制,目前支持0、1、2三代。

    1、新构造添加到堆的对象称为第0代。

    2、经过对第0代的垃圾回收之后,第0代的幸存者被提升至第1代。

    3、经过对第1代的垃圾回收之后,第一代的幸存者被提升至第2代。

    CLR初始化时,会为每一代选择预算。第0代的预算约为256K,第1代预算约2M,第2代预算约10M。在实际使用过程中,垃圾回收器会用类似启发式算法调整各代的预算。

    实例:该实例运行在.NET4.0环境

    View Code
    internal class Program
        {
            private static void Main(string[] args)
            {
                StringBuilder sb = new StringBuilder();
                Console.WriteLine("创建Datatable前:" + GC.GetTotalMemory(true)/(1024) + "K");
                DataTable table = new DataTable("ParentTable");
                Console.WriteLine("创建DataTable后对象代数:" + GC.GetGeneration(table) + "");
                DataColumn column;
                DataRow row;
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.Int32");
                column.ColumnName = "id";
                column.Unique = true;
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "ParentItem";
                column.AutoIncrement = false;
                column.Caption = "ParentItem";
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "ChildrenItem";
                column.AutoIncrement = false;
                table.Columns.Add(column);
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item1";
                column.AutoIncrement = false;
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item2";
                column.AutoIncrement = false;
                table.Columns.Add(column);
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item3";
                column.AutoIncrement = false;
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item4";
                column.AutoIncrement = false;
                table.Columns.Add(column);
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item5";
                column.AutoIncrement = false;
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item6";
                column.AutoIncrement = false;
                table.Columns.Add(column);
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item7";
                column.AutoIncrement = false;
                table.Columns.Add(column);
    
                column = new DataColumn();
                column.DataType = System.Type.GetType("System.String");
                column.ColumnName = "Item8";
                column.AutoIncrement = false;
                table.Columns.Add(column);
                for (int i = 0; i < 2000; i++)
                {
                    row = table.NewRow();
                    row["id"] = i;
                    row["ParentItem"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["ChildrenItem"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item1"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item2"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item3"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item4"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item5"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item6"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item7"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    row["Item8"] = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
    
                    table.Rows.Add(row);
                }
                Console.WriteLine("为Datatable添加数据后占用内存:" + GC.GetTotalMemory(true)/(1024) + "K");
    
                List<Nodes> list = new List<Nodes>();
                Nodes nodes;
                for (int i = 0; i < 2000; i++)
                {
                    nodes = new Nodes();
                    nodes.Id = i;
                    nodes.Name1 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name2 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name3 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name4 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name5 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name6 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name7 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name8 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name9 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    nodes.Name10 = "垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试垃圾回收测试 " + i;
                    list.Add(nodes);
                }
    
                Console.WriteLine("创建List后占用内存:" + GC.GetTotalMemory(true)/(1024) + "K");
                Console.WriteLine("DataTable代数:" + GC.GetGeneration(table));
                Console.WriteLine("List代数:" + GC.GetGeneration(list));
                Console.WriteLine("SB代数:" + GC.GetGeneration(sb));
                Console.ReadLine();
            }
        }
    
        public class Nodes
        {
            public int Id { get; set; }
            public string Name1 { get; set; }
            public string Name2 { get; set; }
    
            public string Name3 { get; set; }
            public string Name4 { get; set; }
            public string Name5 { get; set; }
            public string Name6 { get; set; }
            public string Name7 { get; set; }
            public string Name8 { get; set; }
            public string Name9 { get; set; }
            public string Name10 { get; set; }
    
        }

    运行结果:

    问题提出:

    1、为什么table、list、sb都为2代对象?

    1. 程序的开始创建了StringBuilder的实例sb和DataTable的实例table,由GC.GetGeneration(table)方法可知table和sb这两个对象在刚创建之后属于第0代。
    2. 当通过for循环为table添加数据时很快超出了第0代256K内存的预算,这个时候CLR会启动一次垃圾回收,垃圾回收器检测内存中的sb对象,发现sb对象被后面的Console.WriteLine("SB代数:" + GC.GetGeneration(sb));引用,所以没被回收。由于table占有的内存迅速增加,并且在经过一次垃圾回收后sb幸存下来,所以此时sb和table都被提升到1代。0代内存空出来。
    3. 1代的内存预算是2M,从运行结果可以看出最终table占有的内存为3.5M,也就是说table的数据增加也会超出1代内存预算。在1代内存预算快被超出的时候,CLR启动垃圾回收器,检查第1代和第0代中的所有对象。但发现sb对象仍然被引用,没被回收,所以sb对象在回收第1代和第0代的回收之后幸存下来,所以sb对象被提升到第2代。Table对象由于超过1代的内存预算,也被提升到第2代。1代内存空出来。
    4. 同理也可以得出list对象由于超出1代的内存预算被提升到2代。在这个实例中如果把list的for循环调整到2000000时,会导致OutOfMemoryException异常。因为list占用的内存迅增加,垃圾回收器执行一次完整的回收之后还不能满足list的需要,所以抛出OutOfMemoryException异常。

      对象被提升到2代这个过程中,会多次启动垃圾回收器,对性能有一定的影响,并且由于table和list的数据量比较大,同时也成为大对象。回收大对象损失的性能更多。在这个实例中,从运行结果可以看出2000条的数据量table占用的内存比list占用的内存多340K,这个数量比0代的内存预算还要大。

    建议:

    1、在项目开发中,如果底层不需要用到DataTable自带的一些功能(如select(),compute()等方法),而只是用来数据传输,个人建议采用List<T>的方式。因为它占用的内存比DataTable小,同时在list被回收时性能损失更小。

    2、如果对象有可能为大对象,可以使用GC.GetTotalMemory(true)方法来测定。评估之后可能会成为大对象则建议分割该对象或者采用非托管方式(可以启用unsafe)。

    参考CLR via C#(第三版)第21章 自动内存管理

     
     
     
    标签: CLR垃圾回收
     
     
    分类: .Net
  • 相关阅读:
    Python 爬虫 解决escape问题
    python 爬虫 重复下载 二次请求
    iOS开发-消息通知机制(NSNotification和NSNotificationCenter)
    iOS开发-UITableView自定义Cell
    iOS开发-自定义UIAlterView(iOS 7)
    iOS开发-CocoaPods实战
    iOS开发-UICollectionView实现瀑布流
    iOS开发-UITabBarController详解
    iOS 开发-Certificate、App ID和Provisioning Profile之间的关系
    iOS开发-View中frame和bounds区别
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3051435.html
Copyright © 2011-2022 走看看