C# 值类型和引用类型的内存管理
本次日志,我们来重点聊一聊软件开发过程中,如何提高性能方面的问题。这是软件开发或研发过程中深层次的问题,这篇文章主要从内存分配和内存回收两方面说明,我们软件代码编写过程中,计算如何来工作的。在此你可以了解内存管理的过程和方式,以便在以后的软件开发中注意它、利用它。
值类型包括:int,float,double,bool,结构,引用,表示对象实例的变量
引用类型包括:类和数组;比较特殊的引用类型string、object
一般情况下:值类型存储在堆栈中(不包括包含在引用中的值类型,如类的值字段,类中的引用字段,数组的元素这些都是随引用存储在受管制的堆中);引用类型存储在受管制的堆中,为什么说是受管制的堆,下面会具体来谈。
几个概念:
虚拟内存:32位的计算机,每个进程拥有4G的虚拟内存。
受管制的堆(托管堆):受谁的管制?当然是无用单元收集器,即垃圾收集器。如何管理下面再说?
无用单元收集器:垃圾收集器除了会压缩托管堆、更新引用地址、还会维护托管堆的信息列表等等。
关于值类型的存储先看如下代码:
{
int age=20;
double salary=2000;
}
上面定义的两个变量,int age,告诉编译器需要给我分配4个字节的内存空间来存储age值,它是存储在堆栈上的。堆栈属入先进后出的数据结构,堆栈是从高位地址到低位地址存储数据的。计算机寄存器中保持着一个堆栈指针,他总是指向堆栈最底端的自由空间地址,当我们定义一个int类型的值时,堆栈指针递减四个字节的地址;当变量出了作用域后,堆栈指针相应的相应的递增四个字节的地址,它只是堆栈指针的上下移动,所以堆栈的性能是相当高的。
下面看一下引用类型的存储,还是先看代码:
{
Customer customerA;
customerA=new Customer(); //假定Customer实例占据32个字节
}
上面的代码第一行先声明了一个Customer引用,引用的名字为customerA,在堆栈上给此引用分配存储空间,存储空间的大小为4个字节,因为它只存储了一个引用,这个引用所指向的才是即将存储Customer实例的空间地址,注意此时customerA并没有指向具体的空间,它只是分配了一个空间而已。
第二行执行过程中,.net环境会搜索托管堆,寻找第一个未使用的、连续的32个字节空间分配给类的实例,并设置customerA指向这段空间的顶端位置(堆的空间是从低到高使用的)。当引用变量出作用域后,堆栈中的引用会无效,当托管堆中的实例还在,直到垃圾收集器对其进行清理。
到这里细心的读者可能会有些疑问,是不是定义引用类型时,计算机要搜索整个堆,寻找足够大的内存空间来存储对象呢?这样会不会效率很低?如果没有足够大的连续的空间呢?这个就要谈到“托管”了。堆是受垃圾收集器管理的,.net在执行垃圾收集器时释放能释放的所有对象,并压缩其他对象,然后把所有自由空间组合在一起移动到堆的顶端, 形成连续的块,同时更新其他移动对象的引用。如果再有对象定义,可以很快找到合适的空间。如此看来托管堆工作方式与堆栈类似,它是通过堆指针来完成空间的分配和回收的。
上面谈了.net对内存空间分配的管理过程和方式,接下来谈一谈对内存的回收过程。谈到资源的清理,不得不提到的两个概念和三个方法。
两个概念为:托管资源和非托管资源。
托管资源接受.net framework的CLR(通用语言运行时)的管理;非托管资源则不受它的管理。
三个方法为:Finalize(),Dispose(),Close()。
一、Finalize()为析构方法,清除非托管资源。
在类中定义方式:
public ClassName{
~ClassName()
{
//清理非托管资源(如关闭文件和数据库联接等)
}
}
它的特点是:
1. 运行不确定性。
它是受垃圾收集器的管理,当垃圾收集器工作时,会调用此方法。
2. 性能开销大。
垃圾收集器工作方式为,对象如果执行了Finalize()方法,垃圾收集器第一次执行时,会把它放在一个特殊的队列中;第二次执行的时候才会删除此对象。
3. 不能显示定义和调用,定义为析构方法形式。
基于以上特点,最好不要执行Finalize()方法,除非类确实需要它或与其他两个方法结合来用。
二、Dispose()方法,可清除一切需要清除的资源,包括托管和非托管资源。
定义如下:
public void Dispose()
{
//清理应该清理的资源(包括托管和非托管资源)
System.GC.SuppressFinalize(this); //这一句很重要,下面会解释原因。
}
它的特点:
1. 任何客户代码都应显示调用这个方法,来释放资源。
2. 由于第一点的原因,一般要做一个备份,这个备份一般由析构方法来担任角色。
3. 定义此方法的类,必须继承IDisposable接口。
4. 语法关键字using等同于调用Dispose(),使用using时,它是默认调用Dispose()方法。所以使用using的类也要继承IDisposable接口。
Dispose()方法比较灵活,在资源不需要时立即释放。它是资源的最终处理,调用它意味着会最终删除对象。
三、Close()方法,暂时处置资源的状态,可能以后还会使用。一般处理非托管资源。
定义如下:
public viod Close()
{
//对非托管资源状态的设置,如关闭文件或数据库连接
}
它的特点:
对非托管资源状态的设置,一般是关闭文件或数据库连接。
下面写一个综合且又经典的的例子,利用代码演示一下各部分的作用:(为了省事,这个例子是从网上杜撰来的,只要说明问题就可以了,你说呢。在此应该感谢代码原创者,感谢他写了这么经典和易懂的代码,我们受益匪浅!)
public class ResourceHolder : System.IDisposable
{
public void Dispose()
{
Dispose(true);
System.GC.SuppressFinalize(this);
// 上面一行代码作用是防止"垃圾回收器"调用这个类中的析构方法
// " ~ResourceHolder() "
// 为什么要防止呢? 因为如果用户记得调用Dispose()方法,那么
// "垃圾回收器"就没有必要"多此一举"地再去释放一遍"非托管资源"了
// 如果用户不记得调用呢,就让"垃圾回收器"帮我们去"多此一举"吧 ^_^
// 你看不懂我上面说的不要紧,下面我还有更详细的解释呢!
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// 这里是清理"托管资源"的用户代码段。
}
// 这里是清理"非托管资源"的用户代码段。此处为析构方法的实际执行代码,为了避免客户代码忘记显示调用Dispose()方法,所作的备份。
}
~ResourceHolder()
{
Dispose(false); // 这里是清理"非托管资源"
}
}
如果看不明白以上代码,一定要仔细阅读以下解释,很经典,不看会后悔呦。
这里,我们必须要清楚,需要用户调用的是方法Dispose()而不是方法Dispose(bool),然而,这里真正执行释放工作的方法却并不是Dispose(),而是Dispose(bool) ! 为什么呢?仔细看代码,在Dispose()中,调用了Dispose(true),而参数为"true"时,作用是清理所有的托管资源和非托管资源;大家一定还记得我前面才说过,"使用析构方法是用来释放非托管资源的",那么这里既然Dispose()可以完成释放非托管资源的工作,还要析构方法干什么呢? 其实,析构方法的作用仅仅是一个"备份"!
为什么呢?
格地说,凡执行了接口"IDisposable"的类,那么只要程序员在代码中使用了这个类的对象实例,那么早晚得调用这个类的Dispose()方法,同时,如果类中含有对非托管资源的使用,那么也必须释放非托管资源! 可惜,如果释放非托管资源的代码放在析构方法中(上面的例子对应的是 " ~ResourceHolder() "),那么程序员想调用这段释放代码是不可能做到的(因为析构方法不能被用户调用,只能被系统,确切说是"垃圾回收器"调用),所以大家应该知道为什么上面例子中"清理非托管资源的用户代码段"是在Dispose(bool)中,而不是~ResourceHolder()中! 不过不幸的是,并不是所有的程序员都时刻小心地记得调用Dispose()方法,万一程序员忘记调用此方法,托管资源当然没问题,早晚会有"垃圾回收器"来回收(只不过会推迟一会儿),那么非托管资源呢?它可不受CLR的控制啊!难道它所占用的非托管资源就永远不能释放了吗? 当然不是!我们还有"析构方法"呢! 如果忘记调用Dispose(),那么"垃圾回收器"也会调用"析构方法"来释放非托管资源的!(多说一句废话,如果程序员记得调用Dispose()的话,那么代码"System.GC.SuppressFinalize(this);"则可以防止"垃圾回收器"调用析构方法,这样就不必多释放一次"非托管资源"了) 所以我们就不怕程序员忘记调用Dispose()方法了。

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas





Pemalar juga dipanggil pembolehubah dan apabila ditakrifkan, nilainya tidak berubah semasa pelaksanaan program. Oleh itu, kita boleh mengisytiharkan pembolehubah sebagai pemalar merujuk nilai tetap. Ia juga dipanggil teks. Pemalar mesti ditakrifkan menggunakan kata kunci Const. Sintaks Sintaks pemalar yang digunakan dalam bahasa pengaturcaraan C adalah seperti berikut - consttypeVariableName; ,34, 4567 Pemalar titik terapung - Contoh: 0.0, 156.89, 23.456 Pemalar perlapanan dan Perenambelasan - Contoh: Hex: 0x2a, 0xaa.. Octal

Kod VS dan Visual Studio C++ IntelliSense mungkin tidak dapat mengambil perpustakaan, terutamanya apabila bekerja pada projek besar. Apabila kami menuding pada #Include<wx/wx.h>, kami melihat mesej ralat "Tidak boleh Membuka fail sumber 'string.h'" (bergantung pada "wx/wx.h") dan kadangkala, Fungsi autolengkap tidak bertindak balas. Dalam artikel ini kita akan melihat perkara yang boleh anda lakukan jika VSCode dan VSC++ IntelliSense tidak berfungsi atau mengekstrak perpustakaan. Mengapa Intellisense saya tidak berfungsi dalam C++? Apabila bekerja dengan fail besar, kadangkala IntelliSense

Adakah anda tidak dapat membeli atau menonton kandungan pada Xbox anda kerana kod ralat 8C230002? Sesetengah pengguna terus mendapat ralat ini apabila cuba membeli atau menonton kandungan pada konsol mereka. Maaf, terdapat masalah dengan perkhidmatan Xbox. Cuba lagi nanti. Untuk bantuan dengan isu ini, lawati www.xbox.com/errorhelp. Kod Status: 8C230002 Kod ralat ini biasanya disebabkan oleh masalah pelayan atau rangkaian sementara. Walau bagaimanapun, mungkin terdapat sebab lain, seperti tetapan privasi akaun anda atau kawalan ibu bapa, yang mungkin menghalang anda daripada membeli atau melihat kandungan tertentu. Betulkan Kod Ralat Xbox 8C230002 Jika anda menerima kod ralat 8C semasa cuba menonton atau membeli kandungan pada konsol Xbox anda

Kami mengambil tatasusunan integer Arr[] sebagai input. Matlamatnya adalah untuk mencari elemen terbesar dan terkecil dalam tatasusunan menggunakan kaedah rekursif. Memandangkan kami menggunakan rekursi, kami akan mengulangi keseluruhan tatasusunan sehingga kami mencapai panjang = 1 dan kemudian mengembalikan A[0], yang membentuk huruf asas. Jika tidak, elemen semasa dibandingkan dengan nilai minimum atau maksimum semasa dan nilainya dikemas kini secara rekursif untuk elemen berikutnya. Mari kita lihat pelbagai senario input dan output untuk −Input −Arr={12,67,99,76,32}; Nilai maksimum dalam tatasusunan: 99 Penjelasan &mi

Menurut berita pada 25 Mei, China Eastern Airlines mendedahkan perkembangan terkini pesawat penumpang C919 pada mesyuarat taklimat prestasi. Menurut syarikat itu, perjanjian pembelian C919 yang ditandatangani dengan COMAC telah berkuat kuasa secara rasmi pada Mac 2021, dan pesawat C919 pertama telah dihantar menjelang akhir 2022. Dijangkakan pesawat itu akan mula beroperasi secara rasmi tidak lama lagi. China Eastern Airlines akan menggunakan Shanghai sebagai pangkalan utamanya untuk operasi komersial C919, dan merancang untuk memperkenalkan sejumlah lima pesawat penumpang C919 pada 2022 dan 2023. Syarikat itu menyatakan bahawa rancangan pengenalan masa depan akan ditentukan berdasarkan keadaan operasi sebenar dan perancangan rangkaian laluan. Menurut pemahaman editor, C919 ialah pesawat penumpang satu lorong utama generasi baharu China dengan hak harta intelek bebas sepenuhnya di dunia, dan ia mematuhi piawaian kelayakan udara yang diterima di peringkat antarabangsa. sepatutnya

Memaparkan nombor dalam format yang berbeza adalah salah satu masalah pengekodan asas pembelajaran. Konsep pengekodan yang berbeza seperti pernyataan bersyarat dan pernyataan gelung. Terdapat program yang berbeza di mana kami menggunakan aksara khas seperti asterisk untuk mencetak segi tiga atau segi empat sama. Dalam artikel ini, kami akan mencetak nombor dalam bentuk lingkaran, sama seperti petak dalam C++. Kami mengambil bilangan baris n sebagai input dan bermula dari sudut kiri atas dan bergerak ke kanan, kemudian ke bawah, kemudian ke kiri, kemudian ke atas, kemudian ke kanan lagi, dan seterusnya dan seterusnya. Corak lingkaran dengan nombor 123456724252627282982340414243309223948494431102138474645321120373635343312191817161514

void dalam C ialah kata kunci khas yang digunakan untuk mewakili jenis kosong, yang bermaksud data tanpa jenis tertentu. Dalam bahasa C, void biasanya digunakan dalam tiga aspek berikut. Jenis pulangan fungsi adalah tidak sah Dalam bahasa C, fungsi boleh mempunyai jenis pulangan yang berbeza, seperti int, float, char, dll. Walau bagaimanapun, jika fungsi tidak mengembalikan sebarang nilai, jenis pulangan boleh ditetapkan kepada batal. Ini bermakna bahawa selepas fungsi itu dilaksanakan, ia tidak mengembalikan nilai tertentu. Contohnya: voidhelloWorld()

Menurut Indeks Komuniti Pengaturcaraan TIOBE, salah satu penanda aras untuk mengukur populariti bahasa pengaturcaraan, ia dinilai dengan mengumpul data daripada jurutera, kursus, vendor dan enjin carian di seluruh dunia. Indeks TIOBE pada Januari 2024 telah dikeluarkan baru-baru ini, dan kedudukan bahasa pengaturcaraan rasmi untuk 2023 telah diumumkan memenangi Bahasa Pengaturcaraan TIOBE 2023 Ini adalah kali pertama C# memenangi penghormatan ini dalam tempoh 23 tahun. Siaran akhbar rasmi TIOBE menyatakan bahawa C# telah berada dalam 10 teratas selama lebih daripada 20 tahun Kini ia mengejar empat bahasa utama dan telah menjadi bahasa pengaturcaraan dengan pertumbuhan terbesar dalam satu tahun (+1.43%). Memang layak untuk memenangi anugerah ini. Kedudukan kedua ialah Scratch (+0.83%) dan Fortran (+0
