对.net了解更多点【习惯对List初始化的时候指定容量】
2012-08-29 13:14
393 查看
.net提供了List对象来提供可扩容数据存储,但在使用的过程中相信很多人直接通过默认构造函数进行创建。但这样做会存在一定的风险导致Lis在扩容过程增加CPU的损耗和GC的压力,对于问题的严重性就取决于实际应用的场合,如果在高并发的应用下存在大量这操作那问题就变得严重多了。
首先需要了解一下List的存储机制,在初始化的时候不指定大小的情况是默认分配大小为4的数组,当在添加信息超过该值的情况会进行一个倍分扩容,默认的规则是4,8,16,32...;扩展容的过程中是会构建扩展后大小的数组,并把旧的数据复制过去。ArrayList和List<T>实际的代码大概如下:
通过代码可以明确知道当扩展的时候的确存在创建新数据组和复制,这样问题就来了举一个简单的应用场景数据查询分页返回List,假设每页有20条记录,当我们默认构建List的时候添加20个对象的情况,这个List就要面对3次扩容操作分别是8,16,32. 为了测试这情况分别列举出两中情况的代码
以上两个方法操作在计时上差上好倍,测试时间还不包括GC上的损耗,当在高并发的情况那GC的压力所导致的性能的损失远远不止这个数。 其实在.net中可扩容存储的对象大部分都存在这问题,面对这些问题.net在这些类的使用上也预留一些构造方法来满足实际应用所面对的情况。
当你在写.net程序的时候发现性能上的问题,多看一下.net的相关代码,其实很多情况由于自己的不了解从而导致那样的结果。以上紧紧是一个计时测试,在实际情况中面对这问题我们还要做更多的测试才能找出原因,内存,GC等往往是.NET性能的杀手。
首先需要了解一下List的存储机制,在初始化的时候不指定大小的情况是默认分配大小为4的数组,当在添加信息超过该值的情况会进行一个倍分扩容,默认的规则是4,8,16,32...;扩展容的过程中是会构建扩展后大小的数组,并把旧的数据复制过去。ArrayList和List<T>实际的代码大概如下:
public virtual int Add(object value) { if (this._size == this._items.Length) { this.EnsureCapacity(this._size + 1); } this._items[this._size] = value; this._version++; return this._size++; } private void EnsureCapacity(int min) { if (this._items.Length < min) { int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2); if (num < min) { num = min; } this.Capacity = num; } } public virtual int Capacity { get { return this._items.Length; } set { if (value != this._items.Length) { if (value < this._size) { throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); } if (value > 0) { object[] array = new object[value]; if (this._size > 0) { Array.Copy(this._items, 0, array, 0, this._size); }; this._items = array; return; } this._items = new object[4]; } } }
通过代码可以明确知道当扩展的时候的确存在创建新数据组和复制,这样问题就来了举一个简单的应用场景数据查询分页返回List,假设每页有20条记录,当我们默认构建List的时候添加20个对象的情况,这个List就要面对3次扩容操作分别是8,16,32. 为了测试这情况分别列举出两中情况的代码
static void Test1(object state) { for (int i = 0; i < count; i++) { var items = new List<TestObj>(); for (int k = 0; k < 20; k++) { items.Add(new TestObj()); } } } static void Test2(object state) { for (int i = 0; i < count; i++) { var items = new List<TestObj>(20); for (int k = 0; k < 20; k++) { items.Add(new TestObj()); } } }
以上两个方法操作在计时上差上好倍,测试时间还不包括GC上的损耗,当在高并发的情况那GC的压力所导致的性能的损失远远不止这个数。 其实在.net中可扩容存储的对象大部分都存在这问题,面对这些问题.net在这些类的使用上也预留一些构造方法来满足实际应用所面对的情况。
当你在写.net程序的时候发现性能上的问题,多看一下.net的相关代码,其实很多情况由于自己的不了解从而导致那样的结果。以上紧紧是一个计时测试,在实际情况中面对这问题我们还要做更多的测试才能找出原因,内存,GC等往往是.NET性能的杀手。
相关文章推荐
- Java和guava关于hashmap在初始化的时候最好给个初始容量
- Java和guava关于hashmap在初始化的时候最好给个初始容量,避免扩容引起性能问题的探究。
- 【java】初始化一个指定大小的list,在指定位置set存入元素,下标越界
- ScrollView滚动到指定位置(ScrollView初始化不在顶部的时候可以用)
- 关于ligerUi的ligertree的初始化默认选中指定项目的方法
- python 列表list初始化
- 未能在指定文件夹中创建本地存储区,请选择其他位置。可以检查事件日志以了解详细信息
- 关于C99中的新特性:指定初始化 Designated initializers
- Java中List根据指定字段排序工具类
- List遍历删除 或取指定的前N项
- [ASP]使用RecordSet对象的GetRows方法(返回而为数组)和RS.MaxRecords(指定记录集的最大容量)
- WPF 绑定三(绑定List中指定的字符串)
- 安装android studio时候弹出unable to access android sdk add-on list解决方法
- List 删除指定元素
- python将list连续元素和非连续元素分开转换为指定字符串
- 错误备忘:List的初始化。
- Android OkHttp完全解析 是时候来了解OkHttp了
- 对指定大小的容器使用类类型对象进行初始化的理解
- 当GridView中的一项被点中的时候显示的背景图片:android:listSelector="@drawable/choose_gridview"
- Java工具类_List列表或Array数组按指定大小分组