内存原理介绍

1.       .Net应用程序中的内存

1.1.Net内存类型

Windows使用一个系统:虚拟寻址系统。这个系统的作用是将程序可用的内存地址映射到硬件内存中的实际地址上。其实际结果是在32位的Windows操作系统中,每个进程都可以使用4GB的内存,当然,64位机这个数字就更大了,在这4GB的内存中存储着可执行代码、代码加载的DLL和程序运行的所有变量,这4GB的内存成为虚拟地址空间或虚拟内存。在.Net中要使用多种类型的内存,包括:堆栈、非托管堆和托管堆。

C#将数据分为2种:值数据类型和引用数据类型,值数据类型存储在堆栈中,引用类型存储在内存的托管堆中。

内存格局通常分为4个区:

全局数据区:存放全局变量,静态数据,常量;代码区:存放所有的程序代码;栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等;堆区:即自由存储区

堆栈

堆栈用于存储应用程序执行过程中的局部变量、方法参数、返回值和其他临时值。堆栈按照每个线程进行分配,并作为每个线程完成其工作的一个暂存区。垃圾收集器并不负责清理堆栈,因为为方法调用预留的堆栈会在方法返回时被自动清理。但是请注意,垃圾收集器知道堆栈上存储的对象的引用。当对象在一种方法中被实例化时,该对象的引用(32位或64位整型值,取决于平台类型)将保留在堆栈中,而对象自己却存储在托管堆中,并在变量超出范围时被垃圾收集器收集。

堆栈实际上是向下填充,即由高内存地址向低内存地址填充,堆栈的工作方式是先分配内存的变量后释放即先进后出原则,堆栈中的变量是从下向上释放,这样就保证了堆栈中先进后出的规则不与变量的生命周期起冲突,性能非常高,但是对于所有的变量来说不太灵活,而且变量的生命周期必须嵌套。

非托管堆

非托管堆存储引用类型如类、对象、并受垃圾收集器的控制和管理。用于运行时数据结构、方法表、Microsoft中间语言(MSIL)、JITed代码等。非托管代码根据对象的实例化方式将其分配在非托管堆或堆栈上。托管代码可通过调用非托管的Win32 API或实例化COM对象来直接分配非托管堆内存。CLR出于自身的数据结构和代码原因广泛地使用非托管堆。

托管堆

托管堆是用于分配托管对象的区域,同时也是垃圾收集器的域。CLR使用分代压缩垃圾收集器。垃圾收集器之所以称为分代式,是由于它将垃圾收集后保留下来的对象按生存时间进行划分,这样做有助于提高性能。所有版本的.Net Framework都采用三代分代方法:第0代、第1代、第2代(从年轻代到年老代)。垃圾收集器之所以称为压缩式,是因为它将对象重新定位于托管堆上,从而能够消除漏洞并保持可用内存的连续性。移动大型对象的开销很高,因为垃圾收集器将这些大型对象分配在独立的且不会压缩的大型对象堆上。

堆是从下往上分配,所以自由的空间都在已用空间的上面。

1.2 对象的生命周期

概述

1)  .net对象是被分配到一块叫做托管堆(managed Heap)的内存区域上

2)  New一个对象,返回的是一个指向堆上的引用,而不是真正的对象本身,这个对象保存在栈上

3)  对象管理法则:1、new后不用再管。2、如果托管堆上没有足够的内存,就会进行垃圾回收。

4)  应用程序根:根就是一个存储位置,保存着对堆上一个对象的引用,可以是以下几种类别:

(a)       全局对象的引用(虽然在C#不允许,但是CIL的确允许分配全局对象)

(b)       静态对象/静态字段的引用

(c)       应用程序的代码库的局部对象的引用

(d)       传递进一个方法的对象参数的引用

(e)       等待被终结的对象的引用

(f)        任何引用对象的CPU寄存器

工作原理

在一次垃圾回收过程中,运行库将检查托管堆上的对象,判断应用程序是否仍然可达到它们,即是否有根的。为此,CLR将建立一个对象图,代表堆上可达的每一个对象。没有在对象图中的即为被标记为终结的对象,会从内存中清除,然后,调整可达的对象在内存上的位置,组成一个无冗余并且压缩过的堆。

在堆栈中,一旦变量超出使用范围,其使用的内存空间会被其他变量重新使用,这时其空间中被存储的值将被其他变量覆盖而不复存在,但有时候我们希望这些值仍然存在,这就需要托管堆来实现。

class1 object1;

object1=new class1();

第一句定义了一个class1的引用,实质上只是在堆栈中分配一个4个字节的空间,它将存放后来实例化对象在托管堆中的地址,在windows中这需要4个字节来表示内存地址。

第二句实例化object1对象,实际上是在托管堆中开辟一个内存空间来存储类class1的一个具体的对象,假设这个对象需要36个字节,那么object1指向的实际上是在托管堆一个大小为36个字节的连续内存空间开始的地址。由此可以看出在C#编译器中为什么不允许使用未实例化的对象,因为这个对象在托管堆中还不存在,当对象不再使用时,这个被存储在堆栈中的引用变量将被删除,但是从上述机制可以看出,在托管堆中这个引用指向的对象仍然存在,其空间何时被释放取决垃圾收集器而不是引用变量失去作用域时。

工作方式

进程中每个线程都有自己的堆栈,这是一段线程创建时保留下的地址区域。我们的“栈内存”即再次,至于“堆”内存,个人认为在未用new定义时,堆应该就是未“保留”未“提交”的自由空间,new的功能是在这些自由空间中保留出一个地址范围

栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有FIFO。FIFO的特性,在编译的时候可以指定需要的Stack的大小。在编程中,例如C/C++中,所有的局部变量都是从栈中分配内存空间,实际上也不是什么分配,只是从栈顶向上用就行,在退出函数的时候,只是修改栈指针就可以把栈中的内容销毁,所以速度最快。

堆(Heap)是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程,在C/C++分别用malloc/New请求分配Heap,用free/delete销毁内存。由于从操作系统管理的内存分配所以在分配和销毁的时候要占用时间,所以用堆的效率低的多,但是堆的好处是可以做的很大,C/C++堆分配的Heap是不初始化的。

在Java中除了简单类型(int,char等)都是在堆中分配内存,这也是程序慢的一个主要原因。但是跟C/C++不同,Java中分配的Heap内存是自动初始化的。在Java中所有的对象(包括int的wrapper Integer)都是在堆中分配,但是这个对象的引用却是在Stack中分配。也就是说在建立一个对象时从2个地方都分配内存,在Heap中分配的内存实际建立这个对象,而在Stack中分配的内存只是一个指向这个堆对象的指针(引用)而已。

1.3垃圾回收概念(GC)引入

在使用电脑的过程中大家可能都有过这种经验:电脑用久了以后程序运行会变得越来越慢,其中一个重要原因就是系统中存在大量内存碎片,就是因为程序反复在堆栈中创建和释入变量,久而久之可用变量在内存中将不再是连续的内存空间,为了寻址这些变量将会增加系统开销。在.net中这种情形将得到很大改善,这是因为垃圾收集器的工作,垃圾收集器将会压缩托管堆的内存空间,保证可用变量在一个连续的内存空间内,同时将堆栈中引用变量中的地址修改为新地址,这将会带来额外的系统开销,但是,其带来的好处将会抵消这种影响,而另外一个好处是,程序员将不再花上大量的心思在内存泄漏问题上。

说明,在C#程序中不仅仅只有引用类型的变量,仍然也存在值类型和其他托管堆不能管理的对象,例如文件句柄、网络连接和数据库连接,这些变量的释放仍需要程序员通过析构函数或IDispose接口来做。

1.4参考资料

http://www.tudou.com/home/diary_v9913437.html

http://www.cnblogs.com/skywithcloud/archive/2011/08/12/2136789.html

http://msdn.microsoft.com/zh-cn/magazine/cc163491.aspx

http://blog.csdn.net/directionofear/article/details/8034133

内存分析_.Net内存原理介绍的更多相关文章

  1. 内存分析_.Net垃圾回收介绍

    垃圾回收 1.       .Net垃圾回收中涉及的名称 1.1.什么是代? 垃圾回收器为了提升性能使用了代的机制,共分为三代(Gen0.Gen1.Gen2).GC工作机制基于以下假设, 1)  对象 ...

  2. Linux 内存分析工具的命令大全介绍

    在Linux系统经常被用作服务器系统.当服务器内存吃紧的时候,free命令是我们最常使用的内存分析工具. free使用介绍# free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存, ...

  3. 『Numpy』内存分析_高级切片和内存数据解析

    在计算机中,没有任何数据类型是固定的,完全取决于如何看待这片数据的内存区域. 在numpy.ndarray.view中,提供对内存区域不同的切割方式,来完成数据类型的转换,而无须要对数据进行额外的co ...

  4. 『Numpy』内存分析_利用共享内存创建数组

    引.内存探究常用函数 id(),查询对象标识,通常返回的是对象的地址 sys.getsizeof(),返回的是 这个对象所占用的空间大小,对于数组来说,除了数组中每个值占用空间外,数组对象还会存储数组 ...

  5. Linux 内存分析工具——free命令

    在Linux系统经常被用作服务器系统.当服务器内存吃紧的时候,free命令是我们最常使用的内存分析工具. free使用介绍 free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及 ...

  6. .NET内存分析工具-dotMemory

    .NET内存分析工具-dotMemory 1.介绍 官网链接 引言 程序内存占用较大?内存溢出?需要分析生产环境程序怎么办? dotMemory 使您可以分析各种 .NET 和 .NET Core应用 ...

  7. iOS开发——高级篇——内存分析,Instruments

    一.内存分析 1.静态内存分析(Analyze)不运行程序,直接对代码进行内存分析,查看代码是否有内存泄露优点:分析速度快,并且可以对所有的代码进行内存分析缺点:分析结果不一定准确(没有运行程序,根据 ...

  8. C语言内存分析

    C语言内存分析 一.进制 概念:进制是一种计数方式,是数值的表现形式 4种主要的进制: ①. 十进制:0~9 ②. 二进制:0和1 ③. 八进制:0~7 ④. 十六进制:0~9+a b c d e f ...

  9. AndroidStudio MAT LeakCanary 内存分析之 LeakCanary

    现在我们换一种更清晰方便的方式:LeakCanary https://github.com/square/leakcanary 首先将LeakCanary绑在我们的app上 build.gradle ...

随机推荐

  1. R语言实战读书笔记1—语言介绍

    第一章 语言介绍 1.1 典型的数据分析步骤 1.2 获取帮助 help.start() help("which") help.search("which") ...

  2. Android编程: 界面组成、事件监听器

    学习知识:界面组成.事件监听器 ====界面组成==== 1.用户界面的基本组件叫做View,都是继承android.view.View类,Android里面预定义很多基本的界面组件,比如 Butto ...

  3. Objective-C Foundation框架

    1.字符串 OC由两个字符串:NSString和NSMutableString,NSString代表字符序列不可变的字符串,而NSMutableString则代表字符序列可变的字符串. 1.1 创建字 ...

  4. vs2013中把解决方案上传到SVN服务器

    在VS2013中直接上传代码到SVN服务器,在这之前,必须是你的电脑已经安装了TortoiseSVN. 其次,VS2013必须安装AnkhSVN插件.然后才可以向我下面所述一样使用TortoiseSV ...

  5. UAT测试,PPT测试

    UAT:user acceptable testing 用户验收测试 PPT:product produce test  产品生产验证

  6. 20145103 《Java程序设计》第2周学习总结

    20145103 <Java程序设计>第2周学习总结 教材学习内容总结 在第三章主要学习了Java语言中的类型及其变量主要类型为:整数(1字节的byte,2字节的short,4字节的int ...

  7. struts2 action获取ajax提交数据中文乱码问题

    有个人和我问题相同,地址在这: 解决方法: 在ajax的属性添加这句:contentType:'application/x-www-form-urlencoded; charset=utf-8',

  8. android activity之间传递返回值

    activity A,跳转至 Activity B ,A传参数user_name给B,然后B再返回修改后的参数user_name给A 首先A传user_name给B Intent input_B = ...

  9. 【 Regular Expression Matching 】cpp

    题目: Implement regular expression matching with support for '.' and '*'. '.' Matches any single chara ...

  10. 配置Symfony2

    安装成功后打开server php app/console server:run 127.0.0.1:8000 然后在浏览器输入localhost/8000/config.php进行配置 1.date ...