C#. NET: Memory management Story of the graphic code introduction

Source: Internet
Author: User
Tags finally block
Foreword
The .net runtime uses the garbage collector to automatically process and recycle managed resources. Unmanaged resources require manual coding and processing. Understanding how memory management works can help improve the speed and performance of your applications. Stop talking nonsense and get to the point. The main concepts explained are:








concept
Memory: Also known as virtual memory, or virtual address space, Windows uses a virtual addressing system to automatically map available memory addresses to actual addresses in hardware memory in the background. The result is each of the 32-bit processors Processes 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 by the program when it is running.
Memory stack
区域 An area in the process's virtual memory 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
Garbage collector can automatically process resources in the background
Unmanaged resources
Resources that need to be manually coded and processed through mechanisms or methods such as destructor, Finalize, IDisposable, Using, etc.

Memory stack
The 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 memory stack is understood through the following code:

{// block1 starts
    int a; // solve something
    {// block2 start
       int b; // solve something else
    } // block2 end} // block1 end
Note the above two points:
1) The scope of variables in C # follows the scope declared first and then out of scope, and then declared out of scope first, that is, b is released first, a is released later, and the release order is always the opposite of the order in which they allocate memory.
2) b is in a separate block scope (block2), and the name of the block where a is located is block1, with block2 nested inside.

 please look below:





In stack memory management, a stack pointer is always maintained, which always points to the next available address in the station area, with the name sp, and it is assumed that it points to the address 1000.
The variable a is first pushed into the stack. It is assumed that the machine is 32-bit, and the int type occupies 4 bytes, that is, 997 ~ 1000. After being pushed into the stack, sp points to 996. It can be seen that the growth direction of the memory stack is from high to low.
Then b pushes the stack, occupying 993 ~ 996, sp points to 992. When block 2 is exceeded, the variable b immediately releases the storage on the memory stack, and sp increases by 4 bytes, pointing to 996.
When going out, beyond the block1, the variable a is immediately released. At this time, sp is increased by 4 bytes to point to the original initial address of 1000. Later on the stack, these addresses are occupied again, and then released again. .

Memory heap
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 be nested. In many cases, this requirement is too harsh, because we want some data to be available for a long time after the method exits.
As long as the heap storage space requested by the new operator is used, the latency of the data declaration period is satisfied, such as all reference types. Use managed heap in .net to manage data on memory heap.
托管 The managed heap in .net is different from the heap used by C ++. It works under the control of the garbage collector, while the C ++ heap is low-level.
Now that reference-type data is stored on the managed heap, how are they stored? Please see the code below


void Shout ()
{
   Monkey xingxing; // monkey
   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 we call xingxing an instance of it.

First, declare a Monkey reference, and allocate storage space for this reference on the stack. Remember that this is just a reference, not an actual Monkey object. It's important to remember this! !! !!
Then look at the second line of code:

xingxing = new Monkey ();
What it does: First, it allocates memory on the heap to store Monkey objects, pay attention! !! !! This is a real object, it is not a 4-byte address! !! !! Assume that the Monkey object occupies 64 bytes. These 64 bytes contain the fields of the Monkey instance and some information in .NET to identify and manage the Monkey class instance. These 64 bytes are actually allocated on the memory heap. It is assumed that the addresses on the memory heap are 1937 ~ 2000. The new operator returns a memory address, assuming 997 ~ 1000, and assigns it to xingxing. As follows:




Remember this:
Unlike the memory stack, the memory on the heap is allocated upwards, from low to high addresses.
From the above example, it can be seen that the process of establishing a reference instance is more complicated and the system overhead is greater than the process of establishing a value variable. So since the overhead is so large, what is its advantage? Where is the power of reference data types? ? ?

Look at the following code:

 {// block1
    Monkey xingxing; // monkey
    xingxing = new Monkey ();
    {// block2
      Monkey jingjing = xingxing; // jingjing also references a Monkey object
      // do something
    } // jinjing is out of scope, it is removed from the stack
    // Now only xingxing is still referencing Monkey} // xingxing is out of scope and it is removed from the stack // Now it is not referencing Monkey
Assign the value of a reference instance to another instance of the same type, so the result is that there are two references to the same object Monkey in memory. When an instance goes out of scope, it is removed from the stack, but the data referring to the object remains in the heap until the program terminates, or the garbage collector recycles it, and only the data no longer has any instances referencing it It will be deleted!
A casual example of a practical application:


// Grab data from the interface and put it in the list List <Person> persons = getPersonsFromUI ();
// retrieve these persons from DBList <person> personsFromDB = retrievePersonsFromDB ();
// do something to personsFromDBgetSomethingToPersonsFromDB ();
Will the changes to personsFromDB be timely reflected in the interface?
I can't!
Please see the modified code below:

// Grab data from the interface and put it in the 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 ();
After modification, the data can immediately respond on the interface. Because the person is bound to the UI, all modifications to the person can naturally respond immediately.
This is the power of reference data types. This feature is widely used in C # .NET. This shows that we can have very strong control over the lifetime of the data, because as long as we keep a reference to the data, the data must be on the heap! !! !!
This also shows that the lifetime of the stack-based instance does not match the lifetime of the heap-based object!

Garbage collector GC
碎片 Fragments will form on the memory heap. The .NET garbage collector will compress the memory heap, move objects, and modify all references to objects. This is one of the differences between managed and unmanaged heaps.
The managed heap of .NET only needs to read the value of the heap pointer, but the unmanaged old heap needs to traverse the linked list of addresses to find a place to place new data, so instantiating objects in .NET is much faster.
The first part of the puppet heap is called the 0th generation, and this part resides the latest objects. The old objects left over from the 0th generation garbage collection process are placed on the corresponding part of the 1st generation, and they are recursed in turn. . .

Linked up
The above part is the memory management part of the managed resources, which are automatically executed by .NET in the background. Let's look at the memory management of unmanaged resources. For example, these resources may be UI handles, network connections, file handles, Image objects, etc. .NET does this mainly through three mechanisms. They are destructor, IDisposable interface, and a combination of the two methods to achieve the best processing results. Let ’s take a look.

Destructor
When the C # compiler compiles the destructor, it implicitly compiles the code of the destructor to the code equivalent to the Finalize () method, and determines to execute the finalize () method of the parent class. Look at the following code:

public class Person
{
   ~ Person ()
   {// destructive implementation
   }
}
C # code for IL generated by ~ Person () destructor:

protected override void Finalize ()
{try
   {// destructive implementation
   } finally
   {base.Finalize ();
   }
}
Put in the finally block to ensure that Finalize () of the parent class must be called.
C # destructor is much less used than C ++ destructor because its problem is uncertainty. When a C ++ object is destroyed, its destructor is executed immediately. But because C # uses the garbage collector, it is not possible to determine when the C # object's destructor is executed. If an object occupies precious resources and needs to release resources as soon as possible, you cannot wait for the garbage collector to release them at this time.
时 When a destructor is called for the first time, an object with a destructor needs to call the destructor a second time before the object is actually deleted. If destructors are used frequently, the performance impact can be significant.

IDisposable interface
In C #, it is recommended to use the IDisposable interface instead of the destructor. This mode provides a deterministic mechanism for releasing unmanaged resources, and is not as uncertain as when destructuring.
Assuming that the Person object depends on some external resources and implements the IDisposable interface, if you want to release it, you can:

class Person: IDisposable
{public void Dispose ()
  {// implementation
  }
}

Person xingxing = new Person (); / dom somethingxingxing .Dispose ();
如果 If an exception occurs during the processing of the above code, this code does not release xingxing, so modify it to:

Person xingxing = null; try {
   xingxing = new Person (); // do something} finally {if (xingxing! = null)
    {
        xingxing.Dispose ();
    }
}
C # provides a syntactic sugar called using to simplify the above operations.

using (Person xingxing = new Person ())
{// do something}
The semantics of using here are different from ordinary reference libraries. The function used here just simplifies the code, this syntax sugar can be used less! !! !!
In short, for objects that implement IDisposable, they must manually call the Dispose () method when releasing unmanaged resources. So if you forget it, it will cause a resource leak. As follows:

                Image backImage = this.BackgroundImage;
                if (backImage! = null)
                {
                    backImage.Dispose ();
                    SessionToImage.DeleteImage (_imageFilePath, _imageFileName);

                    this.BackgroundImage = null;

}
In the above example, backImage has been determined to be no longer used, and backImage is read from the physical disk through Image.FromFile (fullPathWay), which is an unmanaged resource, so Dispose () is needed to read the Image in this way. The process is closed. If you forget to write backImage.Dispose (); it will cause a resource leak!

Combining destructor and IDisposable
In general, the best way is to implement two mechanisms to get the advantages of both mechanisms. Because the Dispose () method is called correctly, and the destructor is implemented as a safety mechanism, in case the Dispose () method is not called. Please refer to a mechanism that combines two methods to release managed and unmanaged resources:


public class Person: IDisposable
{private bool isDisposed = false; // Implement IDisposable interface
   public void Dispose ()
   {// true to clean up managed and unmanaged resources
      Dispose (true); // Tell the garbage collector not to call the destructor
      GC.SuppressFinalize (this);
   } protected virtual void Dispose (bool disposing)
   {// isDisposed: whether the object has been cleaned up
      if (! isDisposed)
      {if (disposing)
          {// Clean up managed resources
           } // Clean up unmanaged resources
       }
       isDisposed = true;
   }

   ~ Person ()
   {// false: clean up only unmanaged resources after calling
     // The managed resource will be a separate thread of the garbage collector Finalize ()
     Dispose (false);
   }
}
When the user of this object, directly call the Dispose () method, such as

Person xingxing = new Person (); / do somethingperson.Dispose ();
Call the IDisposable.Dispose () method at this time, specifying that all resources related to the object should be cleaned up, including managed and unmanaged resources.

If the Dispose () method is not called, the managed and unmanaged resources are handled by the destructor.

The above is the content introduced by C # .NET: graphic code for memory management story. For more related content, please pay attention to topic.alibabacloud.com (www.php.cn)!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.