C#内存分配
liuian 2025-08-01 18:40 2 浏览
一 内存分配
C#中的数据类型分为值类型 (Value type) 和引用类型(reference type)。
值 类 型: 所有的值类型都集成自 System.ValueType 上,除非加声明?否则不可为null,保存在 栈(Stack,先进后出)上,常见的值类型有:整形、浮点型、bool、枚举等。
引用类型:所有引用类型都继承自System.Object 上,引用类型保存在 托管堆Heap上,常见的类型有:数组、字符串、接口、委托、object等。
拆箱和装箱:引用类型和值类型的相互转换叫做拆装箱操作。
拆箱:拆箱就是将一个引用型对象转换成任意值型!比如:
int i=0;
System.Object obj=i;
int j=(int)obj;
装箱:装箱就是隐式的将一个值型转换为引用型对象。比如:
int i=0;
Syste.Object obj=i;
二 垃圾回收机制 GC
1 托管资源与非托管资源
GC自动垃圾回收主要是指保存在Heap上的资源,.NET的GC机制有这样两个问题:
1) GC并不是能释放所有的资源。它不能自动释放非托管资源。
2) GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。
GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using语句可以简化资源管理。
托管资源 :托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。
非托管资源:非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标 等。这类资源,
垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。
总结:托管资源是释放由GC来完成,不一定马上回收,一般是系统感觉内存吃紧,会进行紧急回收资源。一个对象想成为被回收,首先需要成为垃圾,GC是通过判断对象及其子对象有没有指向有效的引用,如果没有,GC就认为它是垃圾。垃圾回收机制通过一定的算法得到哪些没有被被引用、或者不再调用的资源,当这些垃圾达到一定的数量时,回启动垃圾回收机制,GC回收实际上是调用了析构函数。
2 垃圾回收算法
大多数CLI使用的是generational, compacting, mark-and-sweep的回收算法:
generational : 经历过垃圾回收且存活下来的对象,更晚地被清理掉。
compacting : 将被回收的对象会被压缩到一起,确保没有脏空间。使用压缩机制使得新对象能快的实例化,而不需要搜索内存找分配空间,同时降低了分页的概率,因为同一页能存储更多对象。另外,垃圾回收会考虑到机器资源,当计算机内存余量较大时很少清理资源。
mark-and-sweep : 每次执行辣鸡回收的时候,都会标记出将要回收的对象。
垃圾回收时对象一共有三代 :0,1,2。每一代都有自己的内存预算,空间不足的时候会调用垃圾回收。为了提高性能都是按代回收,第0代超预算之后就回收第0代的对象,而存活下来的对象就提升为第1代,依次类推,而往往经过多次0代的垃圾回收才能回收一次第1代。
GC进行垃圾回收是系统决定的,下面是进行强制回收的执行代码(非特殊情况下不要使用此方法,会影响系统效率,削弱垃圾回收器中优化引擎的作用,而垃圾回收器可以确定运行垃圾回收的最佳时间)
//对所有代进行垃圾回收。
GC.Collect();
//对指定的代进行垃圾回收。
GC.Collect(int generation);
//强制在 System.GCCollectionMode 值所指定的时间对零代到指定代进行垃圾回收。
GC.Collect(int generation, GCCollectionMode mode);
3 非托管资源的释放
定义一个类时,可以使用两种不同的机制类释放非托管资源,这两种机制有时候放在一起使用。
1)声明析构函数(终结器),作为类的成员
构造函数可以在创建对象实例的时候执行某些操作,析构函数正好相反是资源创建以后被系统回收的时候执行的操作,垃圾回收器在回收对象之前会调用析构函数,所以在函数代码块中可以写释放非托管资源的代码。析构函数没有返回值,没有参数,没有修饰符。
public class AA
{
~AA()
{
//析构函数语法
}
}
析构函数会被编辑器翻译成下面的代码:
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
最终析构函数会被翻译成上面的代码块,重写基类的Finalize()方法,然后最终调用 Base.Finalize()方法。
注意!大量的使用析构函数会影响效率!带有析构函数的对象会被系统执行两次才会被释放掉。GC执行释放资源时,没有析构函数的资源会被直接释放掉,假如目标对象有析构函数,会被先放进一个叫做“终结队列”的
项中去,然后系统调用另一个高优先级线程来执行 Finalize()方法,GC继续回收其它对象。等方法执行完以后会将对象从终结队列中清除出去,此时对象才是真正意义上的垃圾。等GC执行资源回收的时候,才回释放掉终结队列里面的对象。
总结:
托管堆中内存的释放和析构函数的执行分别属于两个不同的线程。
带有析构函数的对象其生命周期会变长,由上知会进行两次垃圾回收处理才能被释放,如此一来将导致程序性能的下降。
若一个对象引用了其他对象时,当此对象不能够被释放时,则其引用对象也就无法进行内存的释放,也就意味着带有析构函数的对象和其引用对象将从第0代提升到第一代,毫无疑问将影响程序的性能。
综上所述,建议是不要实现其析构函数,这将大大降低程序的性能。
2、在类中实现 System.IDisposable 接口
实现IDisposable接口来显示释放系统资源
① 当我们显示调用Dispose()方法以后,会执行释放费托管资源的操作,然后disposedValue会为Flase,所以我们多次调用也没有关系。Dispose()调用执行完以后,执行GC.SuppressFinalize(this)(告诉GC不再执行终结器操作) 代码
② 如果我们不调用 Diapose()方法,系统会调用使用终结器操作,最后也是释放非托管资源。
从例子可以看出,对于手动回收(disposing为true),除了非托管资源,还可以通知其他托管对象Dispose(),因为这时候内部的托管对象肯定没回收。而到了自动回收,就不能通知其他托管对象了,因为垃圾回收可能已经把他们回收了,而且垃圾回收会自动回收他们,也不用你通知了。
总结:
当释放非托管资源时我们应该显式的去实现Dipose()方法或者Close()方法,但是万一我们忘记显式去调用方法,此时还有一条退路,CLR会自动调用Finalize()方法,很显然调用Finalize()方法会大大降低程序的性能,没关系,上述释放模式关键的一点是通过手动释放调用Dispose()方法可以阻止Finalize()方法的调用,换言之,上述通过手动释放既释放了非托管资源又加快了程序运行的速度,毫无疑问,这是一种完美的解决方案。
1、Finalize是系统决定执行的,我们无法干涉。Dispose是可以我们调用来释放的。
2、Finalize只能释放非托管资源,Dispose既可以释放托管资源也可以释放非托管资源。
相关推荐
- 前端开发为什么需要Promise
-
一、引言在Web前端开发中,异步操作是绕不开的话题。随着用户对网页交互性和响应速度要求的不断提高,开发者们不得不处理越来越多的异步任务,如数据获取、文件读写等。本文旨在探讨Promise作为现代Jav...
- 『React』组件副作用,useEffect讲解
-
在React开发中,有时候会听到“副作用”这个词。特别是用到useEffect这个Hook的时候,官方就明确说它是用来处理副作用的。那什么是副作用?为什么我们要专门管控它?今天就聊聊Re...
- 图解 Promise 实现原理(一):基础实现
-
作者:孔垂亮转发链接:https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ前言很多同学在学习Promise时,知其然却不知其所以然,对其中的用法理解不...
- C#实现归并排序与快速排序
-
字数914,阅读大约需5分钟C#实现归并排序与快速排序以下是使用C#实现的归并排序和快速排序算法代码:usingSystem;usingSystem.Collections.Gener...
- C#.NET Newtonsoft.Json 详解
-
简介Newtonsoft.Json(又称Json.NET)是.NET生态中最流行的JSON序列化/反序列化库,支持.NETFramework、.NETCore、Mono、Xamarin...
- C# - 类文件构成,C#基本语法,Console属性与方法 007
-
类文件(.cs)构成类文件主要分为引用命名空间与自己项目的命名空间1)引用命名空间主要是引用类库,分为内部(.Net类库与解决方案内其他项目的命名空间)外部(引用别人的命名空间),之前说过类库的...
- 不要过度使用列表(List): C# 数据结构
-
编程中的每一个决定都会对性能和清晰度产生无声的影响。在C#中,这样重要的选择之一就是选择正确的数据结构。数据结构是基础支柱。这些结构是数据生存、呼吸和交互的地方,决定了代码的效率和可读性。但...
- C# 编程语言 31-40个经典案例
-
案例31:LINQ查询学生成绩排序说明:演示如何使用LINQ查询并排序数据集合。usingSystem;usingSystem.Collections.Generic;usingSyst...
- C#中常用的数据结构
-
写在前面最近在使用.net开发一些程序。它使用的编程语言是C#。我们来看一下它的常用的数据结构有哪些。常用数据结构C#中常见的数据结构:1数组(Array):用于存储固定大小的同类型元素集合...
- C# 编程10个经典案例
-
C#是微软推出的一门现代化、面向对象的高级编程语言,在桌面应用、Web、移动、游戏和云计算等开发领域广泛应用。本篇文章为广大程序员整理了50个必须收藏的经典C#编程案例,助你提升实战能力。案...
- C# 动态数组(ArrayList)
-
动态数组(ArrayList)代表了可被单独索引的对象的有序集合。它基本上可以替代一个数组。但是,与数组不同的是,您可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在...
- c#集合排序
-
在C#中,集合排序是一种常见的操作,它可以帮助我们对集合中的元素进行排序。C#中提供了多种集合排序方法,包括Array.Sort、List.Sort、SortedList和SortedSet等。下面分...
- c#学习手册 (苏素芳等) 高清PDF版
-
《c#学习手册》以初学者为核心,全面介绍了使用c#语言进行程序开发的各种技术。在内容排列上由浅入深,让读者循序渐进地掌握编程技术;在内容讲解上结合丰富的图解和形象的比喻,帮助读者理解“晦涩难懂”的技术...
- C#中的数组探究与学习
-
C#中的数组一般分为:①.一维数组。②.多维数组,也叫矩形数组。③.锯齿数组,也叫交错数组。一.数组定义:数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合,是最基本的数据结构...
- C# 12最新特性解析:代码还能这样写?!微软工程师都惊呆了
-
在C#的持续进化历程中,每一个新版本都宛如一场技术革新的盛宴,C#12更是如此。它所带来的全新特性,不仅刷新了开发者对代码编写方式的认知,甚至连微软工程师们都为之惊叹。今天,就让我们一同深入探索C#...
- 一周热门
-
-
Python实现人事自动打卡,再也不会被批评
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
系统C盘清理:微信PC端文件清理,扩大C盘可用空间步骤
-
飞牛OS入门安装遇到问题,如何解决?
-
10款高性能NAS丨双十一必看,轻松搞定虚拟机、Docker、软路由
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- table.render (33)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)