当前位置: 首页 > news >正文

为什么要学电商网站建设/百度新闻头条新闻

为什么要学电商网站建设,百度新闻头条新闻,宁晋做网站,做 暧视频在线观看网站尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变…

尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变量的行为。在本文中我们将深入理解垃圾回收器,还有如何利用静态类成员来使我们的应用程序更高效。

* 更小的步伐 == 更高效的分配

为了更好地理解为什么更小的足迹会更高效,这需要我们对.NET的内存分配和垃圾回收专研得更深一些。

* 图解:

让我们来仔细看看GC。如果我们需要负责"清除垃圾",那么我们需要拟定一个高效的方案。很显然,我们需要决定哪些东西是垃圾而哪些不是。
为了决定哪些是需要保留的,我们首先假设所有的东西都不是垃圾(墙角里堆着的旧报纸,阁楼里贮藏的废物,壁橱里的所有东西,等等)。假设在我们的生活当中有两位朋友:Joseph Ivan Thomas(JIT)和Cindy Lorraine Richmond(CLR)。Joe和Cindy知道它们在使用什么,而且给了我们一张列表说明了我们需要需要些什么。我们将初始列表称之为"根"列表,因为我们将它用作起始点。我们需要保存一张主列表来记录出我们家中的必备物品。任何能够使必备物品正常工作或使用的东西也将被添加到列表中来(如果我们要看电视,那么就不能扔掉遥控器,所以遥控器将被添加到列表。如果我们要使用电脑,那么键盘和显示器就得添加到列表)。

这就是GC如何保存我们的物品的,它从即时编译器(JIT)和通用语言运行时(CLR)中获得"根"对象引用的列表,然后递归地搜索出其他对象引用来建立一张我们需要保存的物品的图表。

根包括:

* 全局/静态指针。为了使我们的对象不被垃圾回收掉的一种方式是将它们的引用保存在静态变量中。
* 栈上的指针。我们不想丢掉应用程序中需要执行的线程里的东西。
* CPU寄存器指针。托管堆中哪些被CPU寄存器直接指向的内存地址上的东西必须得保留。

Stacking_Heaping1.gif

在以上图片中,托管堆中的对象1、3、5都被根所引用,其中1和5时直接被引用,而3时在递归查找时被发现的。像我们之前的假设一样,对象1是我们的电视机,对象3是我们的遥控器。在所有对象被递归查找出来之后我们将进入下一步--压缩。

* 压缩

我们现在已经绘制出哪些是我们需要保留的对象,那么我们就能够通过移动"保留对象"来对托管堆进行整理。

Stacking_Heaping2.gif

幸运的是,在我们的房间里没有必要为了放入别的东西而去清理空间。因为对象2已经不再需要了,所以GC会将对象3移下来,同时修复它指向对象1的指针。

Stacking_Heaping3.gif

然后,GC将对象5也向下移,

Stacking_Heaping4.gif

现在所有的东西都被清理干净了,我们只需要写一张便签贴到压缩后的堆上,让Claire(指CLR)知道在哪儿放入新的对象就行了。

Stacking_Heaping5.gif

理解GC的本质会让我们明白对象的移动是非常费力的。可以看出,假如我们能够减少需要移动的物品大小是非常有意义的,通过更少的拷贝动作能够使我们提升整个GC的处理性能。

* 托管堆之外是怎样的情景呢?

作为负责垃圾回收的人员,有一个容易出现入的问题是在打扫房间时如何处理车里的东西,当我们打扫卫生时,我们需要将所有物品清理干净。那家里的台灯和车里的电池怎么办?

在一些情况下,GC需要执行代码来清理非托管资源(如文件,数据库连接,网络连接等),一种可能的方式是通过finalizer来进行处理。

None.gifclass Sample
None.gif
ContractedBlock.gifdot.gif
None.gif

在对象创建期间,所有带有finalizer的对象都将被添加到一个finalizer队列中。对象1、4、5都有finalizer,且都已在finalizer队列当中。让我们来看看当对象2和4在应用程序中不再被引用,且系统正准备进行垃圾回收时会发生些什么。

Stacking_Heaping6.gif

对象2会像通常情况下那样被垃圾回收器回收,但是当我们处理对象4时,GC发现它存在于finalizer队列中,那么GC就不会回收对象4的内存空间,而是将对象4的finalizer移到一个叫做"freachable"的特殊队列中。

Stacking_Heaping7.gif

有一个专门的线程来执行freachable队列中的项,对象4的finalizer一旦被该线程所处理,就将从freachable队列中被移除,然后对象4就等待被回收。

Stacking_Heaping8.gif

因此对象4将存活至下一轮的垃圾回收。

由于在类中添加一个finalizer会增加GC的工作量,这种工作是十分昂贵的,而且会影响垃圾回收的性能和我们的程序。最好只在你确认需要finalizer时才使用它。

在清理非托管资源时有一种更好的方法:在显式地关闭连接时,使用IDisposalbe接口来代替finalizer进行清理工作会更好些。

* IDisposable

实现IDisposable接口的类需要执行Dispose()方法来做清理工作(这个方法是IDisposable接口中唯一的签名)。因此假如我们使用如下的带有finalizer的ResourceUser类:

None.gifpublic class ResourceUser 
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif ~ResourceUser() // THIS IS A FINALIZER
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif // DO CLEANUP HERE
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

我们可以使用IDisposable来以更好的方式实现相同的功能:

None.gifpublic class ResourceUser : IDisposable
None.gif
ExpandedBlockStart.gif{
InBlock.gif
ContractedSubBlock.gif IDisposable Members
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

IDisposable被集成在了using块当中。在using()方法中声明的对象在using块的结尾处将调用Dispose()方法,using块之外该对象将不再被引用,因为它已经被认为是需要进行垃圾回收的对象了。

None.gifpublic static void DoSomething()
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gifResourceUser rec = new ResourceUser();
InBlock.gif
InBlock.gifusing (rec)
InBlock.gif
ExpandedSubBlockStart.gif{
InBlock.gif
InBlock.gif // DO SOMETHING 
InBlock.gif
ExpandedSubBlockEnd.gif} // DISPOSE CALLED HERE
InBlock.gif
InBlock.gif // DON'T ACCESS rec HERE
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

我更喜欢将对象声明放到using块中,因为这样可视化很强,而且rec对象在using块的作用域之外将不再有效。这种模式的写法更符合IDisposable接口的初衷,但这并不是必须的。

None.gifpublic static void DoSomething()
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gifusing (ResourceUser rec = new ResourceUser())
InBlock.gif
ExpandedSubBlockStart.gif{
InBlock.gif
InBlock.gif // DO SOMETHING
InBlock.gif
InBlock.gif
InBlock.gif
ExpandedSubBlockEnd.gif} // DISPOSE CALLED HERE
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

在类中使用using()块来实现IDisposable接口,能够使我们在清理垃圾对象时不需要写额外的代码来强制GC回收我们的对象。

* 静态方法

静态方法属于一种类型,而不是对象的实例,它允许创建能够被类所共享的方法,且能够达到"减肥"的效果,因为只有静态方法的指针(8 bytes)在内存当中移动。静态方法实体仅在应用程序生命周期的早期被一次性加载,而不是在我们的类实例中生成。当然,方法越大那么将其作为静态就越高效。假如我们的方法很小(小于8 bytes),那么将其作为静态方法反而会影响性能,因为这时指针比它指向的方法所占的空间还大些。

接着来看看例子...

我们的类中有一个公共的方法SayHello():

None.gifclass Dude
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private string _Name = "Don";
InBlock.gif
InBlock.gif
InBlock.gif
InBlock.gif public void SayHello()
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif                    Console.WriteLine(this._Name + " says Hello");
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

在每一个Dude类实例中SayHello()方法都会占用内存空间。

Stacking_Heaping9.gif

一种更高效的方式是采用静态方法,这样我们只需要在内存中放置唯一的SayHello()方法,而不论存在多少个Dude类实例。因为静态成员不是实例成员,我们不能使用this指针来进行方法的引用。

None.gifclass Dude
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private string _Name = "Don";
InBlock.gif
InBlock.gif
InBlock.gif
InBlock.gif public static void SayHello(string pName)
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif                    Console.WriteLine(pName + " says Hello");
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

Stacking_Heaping10.gif

请注意我们在传递变量时栈上发生了些什么(可以参看<第二部分>)。我们需要通过例子的看看是否需要使用静态方法来提升性能。例如,一个静态方法需要很多参数而且没有什么复杂的逻辑,那么在使用静态方法时我们可能会降低性能。

* 静态变量:注意了!

对于静态变量,有两件事情我们需要注意。假如我们的类中有一个静态方法用于返回一个唯一值,而下面的实现会造成bug:

None.gifclass Counter
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private static int s_Number = 0;
InBlock.gif
InBlock.gif public static int GetNextNumber()
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif int newNumber = s_Number;
InBlock.gif
InBlock.gif // DO SOME STUFF        
InBlock.gif
InBlock.gif                    s_Number = newNumber + 1;
InBlock.gif
InBlock.gif return newNumber;
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

假如有两个线程同时调用GetNextNumber()方法,而且它们在s_Number的值增加前都为newNumber分配了相同的值,那么它们将返回同样的结果!

我们需要显示地为方法中的静态变量锁住读/写内存的操作,以保证同一时刻只有一个线程能够执行它们。线程管理是一个非常大的主题,而且有很多途径可以解决线程同步的问题。使用lock关键字能让代码块在同一时刻仅能够被一个线程访问。一种好的习惯是,你应该尽量锁较短的代码,因为在程序执行lock代码块时所有线程都要进入等待队列,这是非常低效的。

None.gifclass Counter
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private static int s_Number = 0;
InBlock.gif
InBlock.gif public static int GetNextNumber()
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif lock (typeof(Counter))
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif int newNumber = s_Number;
InBlock.gif
InBlock.gif // DO SOME STUFF
InBlock.gif
InBlock.gif                             newNumber += 1;
InBlock.gif
InBlock.gif                             s_Number = newNumber;
InBlock.gif
InBlock.gif return newNumber;
InBlock.gif
ExpandedSubBlockEnd.gif                    }
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

* 静态变量:再次注意了!

静态变量引用需要注意的另一件事情是:记住,被"root"引用的事物是不会被GC清理掉的。我遇到过的一个最烦人的例子:

None.gifclass Olympics
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif public static Collection<Runner> TryoutRunners;
InBlock.gif
ExpandedBlockEnd.gif}
None.gif
None.gif
None.gifclass Runner
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private string _fileName;
InBlock.gif
InBlock.gif private FileStream _fStream;
InBlock.gif
InBlock.gif public void GetStats()
InBlock.gif
ExpandedSubBlockStart.gif {
InBlock.gif
InBlock.gif                    FileInfo fInfo = new FileInfo(_fileName);
InBlock.gif
InBlock.gif                    _fStream = _fileName.OpenRead();
InBlock.gif
ExpandedSubBlockEnd.gif          }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

由于Runner集合在Olympics类中是静态的,不仅集合中的对象不会被GC释放(它们都直接被根所引用),而且你可能注意到了,每次执行GetStats()方法时都会为那个文件开放一个文件流,因为它没有被关闭所以也不会被GC释放,这个代码将会给系统造成很大的灾难。假如我们有100000个运动员来参加奥林匹克,那么会由于太多不可回收的对象而难以释放内存。天啦,多差劲的性能呀!

* Singleton

有一种方法可以保证一个类的实例在内存中始终保持唯一,我们可以采用Gof中的Singleton模式。(Gof:Gang of Four,一部非常具有代表性的设计模式书籍的作者别称,归纳了23种常用的设计模式)

None.gifpublic class Earth
None.gif
ExpandedBlockStart.gif{
InBlock.gif
InBlock.gif private static Earth _instance = new Earth();
InBlock.gif
ExpandedSubBlockStart.gif private Earth() { }
InBlock.gif
ExpandedSubBlockStart.gif public static Earth GetInstance() { return _instance; }
InBlock.gif
ExpandedBlockEnd.gif}
None.gif

我们的Earth类有一个私有构造器,所以Earth类能够执行它的构造器来创建一个Earth实例。我们有一个Earth类的静态实例,还有一个静态方法来获得这个实例。这种特殊的实现是线程安全的,因为CLR保证了静态变量的创建是线程安全的。这是我认为在C#中实现singleton模式最为明智的方式。

* .NET Framework 2.0中的静态类

在.NET 2.0 Framework中我们有一种静态类,此类中的所有成员都是静态的。这中特性对于工具类是非常有用的,而且能够节省内存空间,因为该类只存在于内存中的某个地方,不能在任何情况下被实例化。

* 总结一下...

总的来说,我们能够提升GC表现的方式有:

1. 清理工作。不要让资源一直打开!尽可能地保证关闭所有打开的连接,清除所有非托管的资源。当使用非托管对象时,初始化工作尽量完些,清理工作要尽量及时点。

2. 不要过度地引用。需要时才使用引用对象,记住,如果你的对象是活动着的,所有被它引用的对象都不会被垃圾回收。当我们想清理一些类所引用的事物,可以通过将这些引用设置为null来移除它们。我喜欢采用的一种方式是将未使用的引用指向一个轻量级的NullObject来避免产生null引用的异常。在GC进行垃圾回收时,更少的引用将减少映射处理的压力。

3. 少使用finalizer。Finalizer在垃圾回收时是非常昂贵的资源,我们应该只在必要时使用。如果我们使用IDisposable来代替finalizer会更高效些,因为我们的对象能够直接被GC回收而不是在第二次回收时进行。

4. 尽量保持对象和它们的子对象在一块儿。GC在复制大块内存数据来放到一起时是很容易的,而复制堆中的碎片是很费劲的,所以当我们声明一个包含许多其他对象的对象时,我们应该在初始化时尽量让他们在一块儿。

5. 最后,使用静态方法来保持对象的轻便也是可行的。

Technorati 标签: 垃圾回收

转载于:https://www.cnblogs.com/mirobright/archive/2009/11/21/1607773.html

http://www.lbrq.cn/news/1572301.html

相关文章:

  • 深圳市人民政府网站/google 浏览器
  • 我做网站了/日照seo优化
  • phpcms v9网站建设入门/网络设计
  • 海南房产/百度seo刷排名工具
  • 深圳优质网站建设案例/品牌营销策略有哪些
  • 怎么设置批发网站怎么做/2023年时政热点事件
  • 网站图片多大比较合适/互联网广告怎么做
  • 做pc端网站咨询/seo基础知识培训视频
  • 上海政府门户网站的建设方案/个人建网站的详细步骤
  • 网站建设方案博客/佛山旺道seo优化
  • wordpress 亚马逊s3/seo刷排名工具
  • 徐州网站建设网站制作/去除痘痘怎么有效果
  • 贵阳网站建设公司哪家好/足球排名世界排名
  • 医院网站前置审批文件/拓客app下载
  • 虾皮购物网站怎么做/sem是什么职业
  • 中山市网站建设/免费涨粉工具
  • 象山做网站/百度一下 你就知道首页官网
  • 网站开发东莞/免费b2b网站大全免费
  • 网站建设需要用到那些语言/百度收录链接
  • python做软件的网站/郑州seo技术代理
  • 贵州网站建设公司/福州seo博客
  • wordpress怎么添加备案/来宾网站seo
  • 微信平台的微网站怎么做/微信群推广
  • 怎么做qq空间支付网站/市场调研报告最佳范文
  • 网站建设qq/seo推广培训资料
  • 网站赚取广告费/渠道营销推广方案
  • 跳转网站怎么做/榆林百度seo
  • 吴江网站建设哪家好/哪家网站优化公司好
  • 域名购买国外/湖南有实力seo优化
  • 邢台做移动网站的地方/行业数据统计网站
  • EN 61010电子电气设备安全要求标准
  • epoll模型解析
  • amis表单较验
  • Ansible 实操笔记:Playbook 与变量管理
  • Windows也能用!Claude Code硬核指南
  • 3 Abp 核心框架(Core Framework)