


Preface
.net运行库通过垃圾回收器自动处理回收托管资源,非托管的资源需要手动编码处理。理解内存管理的工作原理,有助于提高应用程序的速度和性能。废话少说,切入正题。主要阐述的概念见下图:

Memory: also known as virtual memory, or virtual Address space , windows uses virtual addressing system, which automatically maps available memory addresses to actual addresses in hardware memory in the background. The result is that each process on a 32-bit processor You can use 4GB of memory to store all parts of the program, including executable code (exe files), all DLLs loaded by the code, and the contents of all variables used when the program is running.
Memory stack In the virtual memory of the process, there is an area where the lifetime of a variable must be nested.
Memory Heap In the virtual memory of the process, data is still available for a long time after the method exits.
Managed resources Resources that the garbage collector can automatically process in the background
Unmanaged resources Requires manual coding, through destructor, Finalize, Resources processed by mechanisms or methods such as IDisposable and Using.
Memory stack
Value type data is stored in the memory stack, and the instance address value of the reference type is also placed in the memory stack (see the discussion of the memory heap). The working principle of the stack can be understood through the following piece of code:{ //block1开始 int a; //solve something {//block2开始 int b; // solve something else }//block2结束}//block1结束Pay attention to 2 points in the above code:
1) The scope of variables in C# follows the principle that what is declared first goes out of scope, and what is declared last is first. Out of scope, that is, b is released first, a is released later, and the release order is always opposite to the order in which they allocate memory.
2) b is in a separate block scope (
block2), and the block name where a is located is block1, which is nested with block2.
Please see the diagram below:

## In stack memory management, a stack pointer is always maintained, which always points to the next available address in the station area, named
sp, as shown in the figure, assuming it points to the number is the address of 1000. Variables a
are first pushed onto the stack. Assume that the computer is 32-bit. The int type occupies 4 bytes, that is, 997~1000. After being pushed onto the stack, sp points to 996. It can be seen that the growth direction of the memory stack is from High address to low address. Then b is pushed onto the stack, occupying 993~996, and sp points to 992. When block block2
is exceeded, variable b immediately releases the storage on the memory stack, and sp is increased by 4 bytes, pointing to 996. When going outward and beyond block block1
, the variable a is released immediately. At this time, sp increases by 4 bytes, pointing to the original initial address of 1000. When it is pushed onto the stack later , these addresses are occupied again, and then released, and the cycle repeats.
Although the stack has very high performance, it is still not very flexible for all variables, because the lifetime of variables located on the memory stack must Nested. In many cases, this requirement is too stringent because we want some data to be available long after the method exits.
As long as the heap storage space is requested using thenew
operator, the data declaration period delay is satisfied, such as all reference types. Use Managed Heap in .net to manage data on the memory heap. The managed heap in .net is different from the heap used in C++. It works under the control of the garbage collector, while the C++ heap is low-level. Since reference type data is stored on the managed heap, how are they stored? Please look at the following code
void Shout() { Monkey xingxing; //猴子类 xingxing = new Monkey(); }
In this code, assume two classes, Monkey and AIMonkey, where the AIMonkey class extends the Monkey object.
Here, we call Monkey an object and xingxing an instance of it.
First, declare a Monkey reference xingxing, and allocate storage space for this reference on the stack. Remember that this is only a reference, not the actual Monkey object. This is important to remember! ! !
Then look at the second line of code:
xingxing = new Monkey();
What it does: First, it allocates memory on the heap to store the Monkey object. Pay attention! ! ! This is a real object, it's not an address that takes up 4 bytes! ! ! Assume that the Monkey object occupies 64 bytes. These 64 bytes contain the fields of the Monkey instance and some information used to identify and manage Monkey class instances in .NET. These 64 bytes are actually allocated on the memory heap, assuming the addresses on the memory heap are 1937~2000. The new operator returns a memory address, assumed to be 997~1000, and assigned to xingxing. The schematic diagram is as follows:

记住一点:
与内存栈不同的是,堆上的内存是向上分配的,由低地址到高地址。
从上面的例子中,可以看出建立引用实例的过程要比建立值变量的过程更复杂,系统开销更大。那么既然开销这么大,它到底优势何在呢?引用数据类型强大到底在哪里???
请看下面代码:
{//block1 Monkey xingxing; //猴子类 xingxing = new Monkey(); {//block2 Monkey jingjing = xingxing; //jingjing也引用了Monkey对象 //do something } //jinjing超出作用域,它从栈中删除 //现在只有xingxing还在引用Monkey}//xingxing超出作用域,它从栈中删除//现在没有在引用Monkey的了
把一个引用实例的值xingxing赋值予另一个相同类型的实例jingjing,这样的结果便是有两个引用内存中的同一个对象Monkey了。当一个实例超出作用域时,它会从栈中删除,但引用对象的数据还是保留在堆中,一直到程序终止,或垃圾回收器回收它位置,而只有该数据不再有任何实例引用它时,它才会被删除!
随便举一个实际应用引用的简单例子:
//从界面抓取数据放到list中List<Person> persons = getPersonsFromUI(); //retrieve these persons from DBList<person> personsFromDB = retrievePersonsFromDB(); //do something to personsFromDBgetSomethingToPersonsFromDB();
请问对personsFromDB的改变,能在界面上及时相应出来吗?
不能!
请看下面修改代码:
//从界面抓取数据放到list中List<Person> persons = getPersonsFromUI(); //retrieve these persons from DBList<Person> personsFromDB = retrievePersonsFromDB(); int cnt = persons.Count;for(int i=0;i<cnt;i++) { persons[i]= personsFromDB [i] ; } //do something to personsFromDBgetSomethingToPersonsFromDB();
修改后,数据能立即响应在界面上。因为persons与UI绑定,所有修改在persons上,自然可以立即响应。
这就是引用数据类型的强大之处,在C#.NET中广泛使用了这个特性。这表明,我们可以对数据的生存期进行非常强大的控制,因为只要保持对数据的引用,该数据就肯定位于堆上!!!
这也表明了基于栈的实例与基于堆的对象的生存期不匹配!
垃圾回收器 GC
内存堆上会有碎片形成,.NET垃圾回收器会压缩内存堆,移动对象和修改对象的所有引用的地址,这是托管的堆与非托管的堆的区别之一。
.NET的托管堆只需要读取堆指针的值即可,但是非托管的旧堆需要遍历地址链表,找出一个地方来放置新数据,所以在.NET下实例化对象要快得多。
堆的第一部分称为第0代,这部分驻留了最新的对象。在第0代垃圾回收过程中遗留下来的旧对象放在第1代对应的部分上,依次递归下去。。。
承上启下
以上部分便是对托管资源的内存管理部分,这些都是在后台由.NET自动执行的。下面看下非托管资源的内存管理,比如这些资源可能是UI句柄,network连接,文件句柄,Image对象等。.NET主要通过三种机制来做这件事。分别为析构函数、IDisposable接口,和两者的结合处理方法,以此实现最好的处理结果。下面分别看一下。
析构函数
C#编译器在编译析构函数时,它会隐式地把析构函数的代码编译为等价于Finalize()方法的代码,并确定执行父类的Finalize()方法。看下面的代码:
public class Person { ~Person() { //析构实现 } }
~Person()析构函数生成的IL的C#代码:
protected override void Finalize() { try { //析构实现 } finally { base.Finalize(); } }
放在finally块中确保父类的Finalize()一定调用。
C#析构函数要比C++析构函数的使用少很多,因为它的问题是不确定性。在销毁C++对象时,其析构函数会立即执行。但由于C#使用垃圾回收器,无法确定C#对象的析构函数何时执行。如果对象占用了 宝贵的资源,而需要尽快释放资源,此时就不能等待垃圾回收器来释放了。
第一次调用析构函数时,有析构函数的对象需要第二次调用析构函数,才会真正删除对象。如果频繁使用析构,对性能的影响非常大。
IDisposable接口
在C#中,推荐使用IDisposable接口替代析构函数,该模式为释放非托管资源提供了确定的机制,而不像析构那样何时执行不确定。
假定Person对象依赖于某些外部资源,且实现IDisposable接口,如果要释放它,可以这样:
class Person:IDisposable { public void Dispose() { //implementation } } Person xingxing = new Person();//dom somethingxingxing .Dispose();
上面代码如果在处理过程中出现异常,这段代码就没有释放xingxing,所以修改为:
Person xingxing = null;try{ xingxing = new Person(); //do something}finally{ if(xingxing !=null) { xingxing.Dispose(); } }
C#提供了一种语法糖,叫做using,来简化以上操作。
using(Person xingxing = new Person()) { // do something}
using在此处的语义不同于普通的引用类库作用。using用在此处的功能,仅仅是简化了代码,这种语法糖可以少用!!!
总之,实现IDisposable的对象,在释放非托管资源时,必须手动调用Dispose()方法。因此一旦忘记,就会造成资源泄漏。如下所示:
Image backImage = this.BackgroundImage; if (backImage != null) { backImage.Dispose(); SessionToImage.DeleteImage(_imageFilePath, _imageFileName); this.BackgroundImage = null; }
在上面那个例子中,backImage已经确定不再用了,并且backImage又是通过Image.FromFile(fullPathWay)从物理磁盘上读取的,是非托管的资源,所以需要Dispose()一下,这样读取Image的这个进程就被关闭了。如果忘记写backImage.Dispose();就会造成资源泄漏!
结合 析构函数和IDisposable这2种机制
一般情况下,最好的方法是实现两种机制,获得这两种机制的优点。因为正确调用Dispose()方法,同时把实现析构函数作为一种安全机制,以防没有调用Dispose()方法。请参考一种结合两种方法释放托管和非托管资源的机制:
public class Person:IDisposable { private bool isDisposed = false; //实现IDisposable接口 public void Dispose() { //为true表示清理托管和非托管资源 Dispose(true); //告诉垃圾回收器不要调用析构函数了 GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { //isDisposed: 是否对象已经被清理掉了 if(!isDisposed) { if(disposing) { //清理托管资源 } //清理非托管资源 } isDisposed = true; } ~Person() { //false:调用后只清理非托管资源 //托管资源会被垃圾回收器的一个单独线程Finalize() Dispose(false); } }
当这个对象的使用者,直接调用了Dispose()方法,比如
Person xingxing = new Person();//do somethingperson.Dispose();
此时调用IDisposable.Dispose()方法,指定应清理所有与该对象相关的资源,包括托管和非托管资源。
如果未调用Dispose()方法,则是由析构函数处理掉托管和非托管资源。
以上就是C#.NET:内存管理story的图文代码介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

C# and .NET provide powerful features and an efficient development environment. 1) C# is a modern, object-oriented programming language that combines the power of C and the simplicity of Java. 2) The .NET framework is a platform for building and running applications, supporting multiple programming languages. 3) Classes and objects in C# are the core of object-oriented programming. Classes define data and behaviors, and objects are instances of classes. 4) The garbage collection mechanism of .NET automatically manages memory to simplify the work of developers. 5) C# and .NET provide powerful file operation functions, supporting synchronous and asynchronous programming. 6) Common errors can be solved through debugger, logging and exception handling. 7) Performance optimization and best practices include using StringBuild

.NETFramework is a cross-language, cross-platform development platform that provides a consistent programming model and a powerful runtime environment. 1) It consists of CLR and FCL, which manages memory and threads, and FCL provides pre-built functions. 2) Examples of usage include reading files and LINQ queries. 3) Common errors involve unhandled exceptions and memory leaks, and need to be resolved using debugging tools. 4) Performance optimization can be achieved through asynchronous programming and caching, and maintaining code readability and maintainability is the key.

Reasons for C#.NET to remain lasting attractive include its excellent performance, rich ecosystem, strong community support and cross-platform development capabilities. 1) Excellent performance and is suitable for enterprise-level application and game development; 2) The .NET framework provides a wide range of class libraries and tools to support a variety of development fields; 3) It has an active developer community and rich learning resources; 4) .NETCore realizes cross-platform development and expands application scenarios.

Design patterns in C#.NET include Singleton patterns and dependency injection. 1.Singleton mode ensures that there is only one instance of the class, which is suitable for scenarios where global access points are required, but attention should be paid to thread safety and abuse issues. 2. Dependency injection improves code flexibility and testability by injecting dependencies. It is often used for constructor injection, but it is necessary to avoid excessive use to increase complexity.

C#.NET is widely used in the modern world in the fields of game development, financial services, the Internet of Things and cloud computing. 1) In game development, use C# to program through the Unity engine. 2) In the field of financial services, C#.NET is used to develop high-performance trading systems and data analysis tools. 3) In terms of IoT and cloud computing, C#.NET provides support through Azure services to develop device control logic and data processing.

.NETFrameworkisWindows-centric,while.NETCore/5/6supportscross-platformdevelopment.1).NETFramework,since2002,isidealforWindowsapplicationsbutlimitedincross-platformcapabilities.2).NETCore,from2016,anditsevolutions(.NET5/6)offerbetterperformance,cross-

The C#.NET developer community provides rich resources and support, including: 1. Microsoft's official documents, 2. Community forums such as StackOverflow and Reddit, and 3. Open source projects on GitHub. These resources help developers improve their programming skills from basic learning to advanced applications.

The advantages of C#.NET include: 1) Language features, such as asynchronous programming simplifies development; 2) Performance and reliability, improving efficiency through JIT compilation and garbage collection mechanisms; 3) Cross-platform support, .NETCore expands application scenarios; 4) A wide range of practical applications, with outstanding performance from the Web to desktop and game development.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

DVWA
Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

SublimeText3 Chinese version
Chinese version, very easy to use

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

Dreamweaver Mac version
Visual web development tools
