[C#技巧]使用using块比没有using块语句消耗更少的内存
作者:admin 时间:2023-5-5 17:46:3 浏览:在前面我们介绍了几个C#性能优化技巧,你可以看出如下文章:
今天我再介绍一些C#技巧,这些方法可以使C#程序消耗更少的内存,从而达到优化性能的目的。
using块比没有using块语句消耗更少的内存
下面的例子可以证明 using
块比没有 using
块语句消耗更少的内存。
我们知道,如果我们实现一个 using
块,代码大小可能会更大,因为 using
块在内部会在 IL 代码中创建一个 try ... catch
,但是一旦它在运行时在 IL 代码中实现,它就会有效地处理系统内存。为了证明这一点,我编写了一个简单的程序,如下所示。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Globalization;
using System.Data.SqlClient;
namespace Test1
{
class Test
{
public void Test1()
{
StreamWriter wr = new StreamWriter(@"D:\text.txt");
}
public void Test2()
{
using (StreamWriter wr = new StreamWriter(@"D:\abc.txt"))
{
}
}
}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.Test1();
t.Test2();
}
}
}
在输出部分,我组合了三个输出屏幕。
在分配图中,我们看到 using
块比没有 using
块消耗更少的资源,因为如果我们实现 using
块,程序可以有效地管理内存。
通常高级别的方法,其速度越慢
通常,你使用的方法级别越高,它就越慢。我在这里发现的一个常见示例是,当处于代码的繁忙部分(可能在被调用数百万次的循环中)时使用 LINQ
。LINQ
非常适合快速表达可能需要大量代码行的内容,但大家经常会把性能搁置一旁。
别误会我的意思——LINQ 非常适合开发出可运行的应用程序。但是在代码库中以性能为中心的部分,可能会放弃太多。特别是因为将如此多的操作链接在一起非常容易。
我自己的具体示例涉及 .SelectMany().Distinct().Count()
,鉴于它被调用了数千万次,它累积了大量的运行时间。我采用了另一种方法,将执行时间减少了几个数量级。
- 虽然高级别的方法看起来更简洁、更快,但它也可以隐藏不必要的复杂性和冗余运行时。
不要使用空析构函数
标题说明了一切——不要在你的类中添加空的析构函数。对于每个具有析构函数的类,都会将一个条目添加到 Finalize 队列中。然后,垃圾收集器 (GC) 在调用析构函数的时候调用处理队列。一个空的析构函数意味着这一切都是无用的。
请记住,正如我们已经提到的,GC 执行在性能方面并不便宜。不要不必要地为 GC 工作。
- 避免使用空的析构函数来保留 GC:除非必要,否则不要使用终结器,并在需要时使用 SafeHandle。
避免不必要的分配
在这里,我将重点关注一个技巧:避免不必要的分配。这意味着要避免这样的事情:
List<Product> products = new List<Product>();
products = productRepo.All();
第一行创建了一个完全无用的列表实例,因为紧接着的下一行返回另一个实例并将其引用分配给变量。现在想象上面的两行代码在一个执行数千次的循环中?
不要关注示例本身,而是关注一般建议:
- 除非确实需要,否则不要创建对象。
C#/.NET 具有垃圾收集功能,该过程确定哪些对象当前已过时并将其删除以释放内存空间。这意味着在 C# 中,与 C++ 等语言不同,你不必手动处理删除不再有用的对象以声明其内存空间的问题。相反,垃圾收集器 (GC) 会处理所有这些,因此你不必这样做。
问题是天下没有免费的午餐,收集过程本身会导致性能损失,所以你真的不希望 GC 一直收集,那么你就应该尽量避免这种情况。
避免不必要的装箱和拆箱
装箱和拆箱——就像垃圾收集——在性能方面是昂贵的过程。因此,我们希望避免不必要地包含它们。
装箱就像创建一个引用类型的框并在其中放入一个值类型的值。换句话说,它包括将值类型转换为“对象”或此值类型实现的接口类型。拆箱正好相反——它打开盒子并从里面提取值类型。
然而,装箱和拆箱本身就是昂贵的过程。除此之外,当装箱一个值时,就会在堆上创建另一个对象,这会给 GC 带来额外的压力。
那么,如何避免装箱和拆箱呢?
通常,可以通过避免 .NET(1.0 版)中早于泛型的旧 API 来实现这一点,因此必须依赖于使用对象类型。例如,更喜欢 System.Collections.Generic.List
之类的通用集合,而不是 System.Collections.ArrayList
之类的集合。
- 装箱对于将小值类型视为引用类型非常有效,并且可以简化你的编码过程,但会以性能为代价。
- 避免使用依赖于对象类型的旧 API。
总结
本文介绍了一些C#技巧,这些方法可以使C#程序消耗更少的内存,从而达到优化性能的目的。
相关文章
- 站长推荐