百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

C#内存分配

liuian 2025-08-01 18:40 25 浏览

一 内存分配

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既可以释放托管资源也可以释放非托管资源。

相关推荐

赶紧收藏!编程python基础知识,本文给你全部整理好了

想一起学习编程Python的同学,趁我粉丝少,可以留言、私信领编程资料~Python基础入门既然学习Python,那么至少得了解下这门编程语言,知道Python代码执行过程吧。Python的历...

创建绩效改进计划 (PIP) 的6个步骤

每个经理都必须与未能达到期望的员工抗衡,也许他们的表现下降了,他们被分配了新的任务并且无法处理它们,或者他们处理了自己的任务,但他们的行为对他人造成了破坏。许多公司转向警告系统,然后在这些情况下终止。...

PI3K/AKT信号通路全解析:核心分子、上游激活与下游效应分子

PI3K/AKT/mTOR(PAM)信号通路是真核细胞中高度保守的信号转导网络,作用于促进细胞存活、生长和细胞周期进程。PAM轴上生长因子向转录因子的信号传导受到与其他多条信号通路的多重交叉相互作用的...

互联网公司要求签PIP,裁员连N+1都没了?

2021年刚画上句号,令无数互联网公司从业者闻风丧胆的绩效公布时间就到了,脉脉上已然炸了锅。阿里3.25、腾讯二星、百度四挡、美团绩效C,虽然名称五花八门,实际上都代表了差绩效。拿到差绩效,非但不能晋...

Python自动化办公应用学习笔记3—— pip工具安装

3.1pip工具安装最常用且最高效的Python第三方库安装方式是采用pip工具安装。pip是Python包管理工具,提供了对Python包的查找、下载、安装、卸载的功能。pip是Python官方提...

单片机都是相通的_单片机是串行还是并行

作为一个七年的从业者,单片机对于我个人而言它是一种可编程的器件,现在长见到的电子产品中几乎都有单片机的身影,它们是以单片机为核心,根据不同的功能需求,搭建不同的电路,从8位的单片机到32位的单片机,甚...

STM32F0单片机快速入门八 聊聊 Coolie DMA

1.苦力DMA世上本没有路,走的人多了,便成了路。世上本没有DMA,需要搬运的数据多了,便有了DMA。大多数同学应该没有在项目中用过这个东西,因为一般情况下也真不需要这个东西。在早期的单片机中...

放弃51单片机,直接学习STM32开发可能会面临的问题

学习51单片机并非仅仅是为了学习51本身,而是通过它学习一种方法,即如何仅仅依靠Datasheet和例程来学习一种新的芯片。51单片机相对较简单,是这个过程中最容易上手的选择,而AVR单片机则更为复杂...

STM32串口通信基本原理_stm32串口原理图

通信接口背景知识设备之间通信的方式一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。并行与串行通信的区别如下表所示。串行通信的分类1、按照数据传送方向,分为:单工:数据传输只支持数据在一个...

单片机的程序有多大?_单片机的程序有多大内存

之前一直很奇怪一个问题,每次写好单片机程序之后,用烧录软件进行烧录时,能看到烧录文件也就是hex的文件大小:我用的单片机芯片是STM32F103C8T6,程序储存器(flash)只有64K。从...

解析STM32单片机定时器编码器模式及其应用场景

本文将对STM32单片机定时器编码器模式进行详细解析,包括介绍不同的编码器模式、各自的优缺点以及相同点和不同点的应用场景。通过阅读本文,读者将对STM32单片机定时器编码器模式有全面的了解。一、引言...

两STM32单片机串口通讯实验_两个32单片机间串口通信

一、实验思路连接两个STM32单片机的串口引脚,单片机A进行发送,单片机B进行接收。单片机B根据接收到单片机A的指令来点亮或熄灭板载LED灯,通过实验现象来验证是否通讯成功。二、实验器材两套STM32...

基于单片机的智能考勤机设计_基于51单片机的指纹考勤机

一、设计背景随着科技水平的不断发展,在这么一个信息化的时代,智能化信息处理已是提高效率、规范管理和客观审查的最有效途径。近几年来,国内很多公司都在加强对企业人员的管理,考勤作为企业的基础管理,是公司...

STM32单片机详细教学(二):STM32系列单片机的介绍

大家好,今天给大家介绍STM32系列单片机,文章末尾附有本毕业设计的论文和源码的获取方式,可进群免费领取。前言STM32系列芯片是为要求高性能、低成本、低功耗的嵌入式应用设计的ARMCortexM...

STM32单片机的 Hard-Fault 硬件错误问题追踪与分析

有过单片机开发经验的人应该都会遇到过硬件错误(Hard-Fault)的问题,对于这样的问题,有些问题比较容易查找,有些就查找起来很麻烦,甚至可能很久都找不到问题到底是出在哪里。特别是有时候出现一次,后...