Microsoft > Visual Studio 论坛 > Visual C# > 如何用c#开发高性能的软件

已答复 如何用c#开发高性能的软件

  • 2010年4月29日 10:29
     
     

    如何用c#开发高性能的软件

    我用 C#写了个程序,运行后,占用内存很大,时间越长,占用越多.如何进行资源释放和垃圾回收呢?


    田田qq:764574267

答案

  • 2010年4月30日 1:42
    版主
     
     已答复

    一下回复节选自《程序员面试指南》,你可以检查一下这些方面的问题,你的问题很可能是不断地创建新对象,而垃圾回收器来不及回收导致的,你看看是否有在循环中不断创建新对象的情况:

    在一些不规范的编码情况下,即使是拥有垃圾回收和内存管理机制的托管堆上,仍然可能出现内存泄漏。首先读者需要了解什么是内存泄漏。
        内存泄漏是指内存空间上产生了不再被实际使用却又不能被分配的内存,内存泄漏的意义很广泛,例如程序无意义地保持对象内存、内存碎片、不彻底的对象释放等 都属于内存泄漏现象。内存泄漏将导致主机的内存随程序的运行而逐渐减少,无论内存泄漏的具体形式如何,它的危害都是显而易见的,所有的内存泄漏都是需要程 序员努力避免的。
    按照内存泄漏的定义,大部分时候.NET的托管堆中存在着短暂的内存泄漏情况,那是因为对象一旦不被使用后,需要等到下一个GC 时才能被释放。这里,笔者将举出其他几种对系统危害更大的内存泄漏情况:
    1)大对象的分配
    .NET中所有的大对象将分配在托管堆内一个特 殊的区域,这里暂时称呼它为“大对象堆”。在回收大对象堆内的对象时,其他的大对象不会被移动,这是考虑到大规模地移动对象需要耗费过多的资源。这样,在 程序过多地分配和释放大对象之后,就会产生很多内存碎片。
    ,随着对象的分配和释放不断进行,在不进行对象移动的大对象堆内,将不可避免地产生小的 内存碎片。程序员对此能做的就是尽量减少大对象的分配次数,尤其是那些作为局部变量的,将被大规模分配和释放的大对象,典型的例子就是String类型, 关于String的使用在后续章节将有详细的叙述。
    2)不恰当地保存根引用
    最简单也最常见的错误做法可能就是不恰当地把一个对象申明为公 共静态变量,一个公共的静态变量将一直被GC视为一个在使用的根引用,而更糟糕的情况是,当这个对象内部还包含更多的对象引用时,这些对象同样不会被释 放。看一段简单的代码:代码3-19。
    代码3-19  保存根引用:RefRoot.cs
    namespace NET.MST.Third.RootRef
    {
        public class RefRoot
        {
            //这里是一个占用大量内存的成员
            public String[] BigMembor;
            public RefRoot(String content)
            {
                //初始化大对象
                BigMembor = new String[1000];
                for (int i = 0; i < BigMembor.Length; i++)
                    BigMembor[i] = content;
            }
        }
        public class MainClass
        {
           //公共静态大对象
            public static RefRoot bigobject = new RefRoot("aaa");
            static void Main()
            {
                Console.Read();
            }
        }
    }
    在代码3-19中,MainClass定义了一个公共静态的大对象,这个对象将直至程序运行结束后才会被GC释放。这一段代码的设 计是否成功将取决于这个公共静态变量被使用的频率以及业务逻辑,如果在整个程序中各个类型不断地使用这个静态成员,那这样的设计有助于减少大对象堆内的内 存碎片,但如果整个程序极少地甚至于只有一次使用了这个成员,那考虑到它占用的内存会影响整体系统的性能,设计时应该考虑把他设计成实例变量,以便于GC 能够及时地释放它。
    这里仍然只从性能的角度考虑问题,实际的设计情况不仅需要考虑性能,还需要考虑程序的和架构和可扩展性。
    3)3)不正 确的Finalize方法
    在前述章节中笔者已经介绍了,Finalize方法由一个专用的线程进行调用,微软并没有公开这部分具体的调度算法,但 是有一点却是肯定的,不正确的Finalize方法将导致Finalize方法不能被正确执行,系统中所有的Finalize方法不能被正确执行时,包含 它们的对象也只能驻留在托管堆内不能被释放,这样的情况将会导致严重的后果。
    Finalize方法应该只致力于快速而简单地释放非托管资源,并且 尽可能快地返回。不正确的Finalize方法可能包含这样的代码:
    ?    没有保护地写文件日志
    ?    访问数据库
    ?    访问网络
    ?    把当前对象赋给某个存活的引用
    当Finaize方法访问文件系统、数据库系统或者网络时,将会有资源争用和等待的 潜在危险。试想一个试图不断尝试访问离线数据库的Finalize方法,将会在长时间内不会返回,这不仅影响了本身对象的释放,也使得排在 Finalize方法队列中的所有后续对象得不到释放,这个连锁反应将很快造成内存耗尽。
    而另外一种危险的代码是在Finalize方法中把对象 自身又赋给了另外一个存活的引用,这时对象内的一部分资源已经被释放了,而另外一部分则没有,这样一个对象被再次激活后,将导致不可预知的后果。

     


    周雪峰

全部回复

  • 2010年4月29日 11:04
     
     

    想要速度性能 用c或者c++开发

    想要开发效率 可以用.net  我相信过十年后 不会有人再说net占内存  速度慢之类问题


    77138191 .net与asp.net qq群 9861961 silverlight群
  • 2010年4月29日 11:06
    版主
     
     

    你好!

         .NET下有很好的垃圾回收机制,通常情况下不容易发生内存泄漏!

         你主要检查一下使用了非托管资源的实例,是否都使用Dispose()方法释放了资源!


    周雪峰
  • 2010年4月29日 15:06
     
     
    C#会自动回收不再使用的托管类型,你也可以使用GC来手动实现回收垃圾。不过性能方面看你自己怎么想,要说C++、C,那用汇编效率更高,所以个人认为,效率是相对的。
  • 2010年4月29日 22:57
     
     

    Update 一下版主,.NET 也是会有内存泄露的。

    请参考一下:

    http://lzy.javaeye.com/blog/344317

    这个说得很详细,你这种情况一定是发生了内存泄露。

  • 2010年4月29日 22:59
     
     
    也请参考:http://baike.baidu.com/view/3539539.htm?func=retitle
  • 2010年4月30日 1:42
    版主
     
     已答复

    一下回复节选自《程序员面试指南》,你可以检查一下这些方面的问题,你的问题很可能是不断地创建新对象,而垃圾回收器来不及回收导致的,你看看是否有在循环中不断创建新对象的情况:

    在一些不规范的编码情况下,即使是拥有垃圾回收和内存管理机制的托管堆上,仍然可能出现内存泄漏。首先读者需要了解什么是内存泄漏。
        内存泄漏是指内存空间上产生了不再被实际使用却又不能被分配的内存,内存泄漏的意义很广泛,例如程序无意义地保持对象内存、内存碎片、不彻底的对象释放等 都属于内存泄漏现象。内存泄漏将导致主机的内存随程序的运行而逐渐减少,无论内存泄漏的具体形式如何,它的危害都是显而易见的,所有的内存泄漏都是需要程 序员努力避免的。
    按照内存泄漏的定义,大部分时候.NET的托管堆中存在着短暂的内存泄漏情况,那是因为对象一旦不被使用后,需要等到下一个GC 时才能被释放。这里,笔者将举出其他几种对系统危害更大的内存泄漏情况:
    1)大对象的分配
    .NET中所有的大对象将分配在托管堆内一个特 殊的区域,这里暂时称呼它为“大对象堆”。在回收大对象堆内的对象时,其他的大对象不会被移动,这是考虑到大规模地移动对象需要耗费过多的资源。这样,在 程序过多地分配和释放大对象之后,就会产生很多内存碎片。
    ,随着对象的分配和释放不断进行,在不进行对象移动的大对象堆内,将不可避免地产生小的 内存碎片。程序员对此能做的就是尽量减少大对象的分配次数,尤其是那些作为局部变量的,将被大规模分配和释放的大对象,典型的例子就是String类型, 关于String的使用在后续章节将有详细的叙述。
    2)不恰当地保存根引用
    最简单也最常见的错误做法可能就是不恰当地把一个对象申明为公 共静态变量,一个公共的静态变量将一直被GC视为一个在使用的根引用,而更糟糕的情况是,当这个对象内部还包含更多的对象引用时,这些对象同样不会被释 放。看一段简单的代码:代码3-19。
    代码3-19  保存根引用:RefRoot.cs
    namespace NET.MST.Third.RootRef
    {
        public class RefRoot
        {
            //这里是一个占用大量内存的成员
            public String[] BigMembor;
            public RefRoot(String content)
            {
                //初始化大对象
                BigMembor = new String[1000];
                for (int i = 0; i < BigMembor.Length; i++)
                    BigMembor[i] = content;
            }
        }
        public class MainClass
        {
           //公共静态大对象
            public static RefRoot bigobject = new RefRoot("aaa");
            static void Main()
            {
                Console.Read();
            }
        }
    }
    在代码3-19中,MainClass定义了一个公共静态的大对象,这个对象将直至程序运行结束后才会被GC释放。这一段代码的设 计是否成功将取决于这个公共静态变量被使用的频率以及业务逻辑,如果在整个程序中各个类型不断地使用这个静态成员,那这样的设计有助于减少大对象堆内的内 存碎片,但如果整个程序极少地甚至于只有一次使用了这个成员,那考虑到它占用的内存会影响整体系统的性能,设计时应该考虑把他设计成实例变量,以便于GC 能够及时地释放它。
    这里仍然只从性能的角度考虑问题,实际的设计情况不仅需要考虑性能,还需要考虑程序的和架构和可扩展性。
    3)3)不正 确的Finalize方法
    在前述章节中笔者已经介绍了,Finalize方法由一个专用的线程进行调用,微软并没有公开这部分具体的调度算法,但 是有一点却是肯定的,不正确的Finalize方法将导致Finalize方法不能被正确执行,系统中所有的Finalize方法不能被正确执行时,包含 它们的对象也只能驻留在托管堆内不能被释放,这样的情况将会导致严重的后果。
    Finalize方法应该只致力于快速而简单地释放非托管资源,并且 尽可能快地返回。不正确的Finalize方法可能包含这样的代码:
    ?    没有保护地写文件日志
    ?    访问数据库
    ?    访问网络
    ?    把当前对象赋给某个存活的引用
    当Finaize方法访问文件系统、数据库系统或者网络时,将会有资源争用和等待的 潜在危险。试想一个试图不断尝试访问离线数据库的Finalize方法,将会在长时间内不会返回,这不仅影响了本身对象的释放,也使得排在 Finalize方法队列中的所有后续对象得不到释放,这个连锁反应将很快造成内存耗尽。
    而另外一种危险的代码是在Finalize方法中把对象 自身又赋给了另外一个存活的引用,这时对象内的一部分资源已经被释放了,而另外一部分则没有,这样一个对象被再次激活后,将导致不可预知的后果。

     


    周雪峰
  • 2010年4月30日 16:29
     
     

    与c和c++相比,c#(包括其他托管项目)没有性能可言,相差了不只一点点。但随着机器性能的提高,完成任务是没有问题的。

    .net的程序占用内存大,这点可以肯定,初期也会慢慢变大(等gc回收),之后就维持一定的水平了。如果一直增大,则肯定是程序的原因,需要改良程序。

    可以考虑依次把某一个模块或程序循环执行1000次,而其他的都不变,看看哪个最败家……


    霸王
  • 2010年5月2日 5:39
     
     
    这可不一定 .NET对CPU特殊指令和并行处理的支持是优于本地代码的。何况C++的速度能否体现也得建立在程序员水平的基础上。
  • 2010年5月2日 12:48
     
     

    当然是在简单优化过的情况下来说的,毕竟c++程序员的差别尤如天地,而c#的差别不到哪里去,这有好处也有坏处。

    所以一般使用native c来写核心算法(有很强的优化),外围用c#来封装,目前来看是比较理想的方式。

    不过,这一切跟决策层相比又是可以忽略的……因此,在系统负载可以接受的情况下,没必要太在意性能问题,把重点放到构架上比较好。


    霸王