内存分析_.Net内存原理介绍

内存原理介绍

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内存原理介绍,布布扣,bubuko.com

时间: 2024-10-28 14:31:06

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

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

垃圾回收 1.       .Net垃圾回收中涉及的名称 1.1.什么是代? 垃圾回收器为了提升性能使用了代的机制,共分为三代(Gen0.Gen1.Gen2).GC工作机制基于以下假设, 1)  对象越新,生存期越短 2)  对象越老,生存期越长 3)  回收堆的一部分比回收整个堆时间短 在应用程序的生命周期中,最近新建的对象被分配在第0代,在一次垃圾回收之后存活下来的进入下一代.这样可以使GC专注于回收最有可能存在更多可回收对象的第0代(最近分配的最有可能很快被释放) 1.2 什么时候发生垃圾

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

在Linux系统经常被用作服务器系统.当服务器内存吃紧的时候,free命令是我们最常使用的内存分析工具. free使用介绍# free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一. free命令使用起来非常简单. Copy # 命令格式 free [参数] # 可选参数 -b 以Byte为单位显示内存使用情况. -k 以KB为单位显示内存使用情况. -m 以MB为单位显示内存使用情况

『Python』内存分析_List对象内存占用分析

『Python』内存分析_下_list和array的内存增长模式 list声明后结构大体分为3部分,变量名称--list对象(结构性数据+指针数组)--list内容,其中id表示的是list对象的位置, v引用变量名称,v[:]引用list对象,此规则对python其他序列结构也成立,以下示范可用id佐证, a=b时,a和b指向同一个list对象 a=b[:]时,a的list对象和b的list对象指向同一个list内容 Q1:元素存储地址是否连续 首先见得的测试一下list对象存储的内容(结构3

JVM核心机制_类加载的全过程_java的内存分析_初始化时机。

学习java的时间不短了,度过了是追求怎么用的阶段,我想在编程这条路上走的更踏实.更远. 一 JVM运行的基本过程 核心就是类加载的过程 jvm 将class文件中的字节码内容加载到内存当中,并对内存进行链接(校验.准备.解析),然后初始化,最总形成jvm可直接使用的java类型的过程. 加载:静态 字节码========>内存当中, 将静态的数据 ===>方法区中的 运行时数据结构, 并 在堆中生成一个代表这个类的java.lang.Class对象 为方法区类数据的访问接口.  这个过程需要

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

引.内存探究常用函数 id(),查询对象标识,通常返回的是对象的地址 sys.getsizeof(),返回的是 这个对象所占用的空间大小,对于数组来说,除了数组中每个值占用空间外,数组对象还会存储数组长度.数组类型等其他信息 numpy.ndarray.ctypes.data属性,返回numpy数组的内存位置 array.array.buffer_info(),数组对象的内存信息,返回元素起始地址和元素个数 help(array.buffer_info)'''buffer_info(self,

『Numpy』内存分析_numpy.dtype内存数据解析方式指导

numpy.dtype用于自定义数据类型,实际是指导python程序存取内存数据时的解析方式. [注意],更改格式不能使用 array.dtype=int32 这样的硬性更改,会不改变内存直接该边解析过程,导致读取出问题,所以使用 array.astype(int32) ,这样才安全. 一.基本使用示例 // 定义一个数据类型,其中name为16为字符串,grades为2个float64的子数组 >>> dt = np.dtype([('name', np.str_, 16), ('gr

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

在Linux系统经常被用作服务器系统.当服务器内存吃紧的时候,free命令是我们最常使用的内存分析工具. free使用介绍 free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一. free命令使用起来非常简单. # 命令格式 free [参数] # 可选参数 -b 以Byte为单位显示内存使用情况. -k 以KB为单位显示内存使用情况. -m 以MB为单位显示内存使用情况. -g 以

Linux 内存分析工具—free命令

在Linux系统经常被用作服务器系统.当服务器内存吃紧的时候,free命令是我们最常使用的内存分析工具. free使用介绍 free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一. free命令使用起来非常简单. # 命令格式 free [参数] # 可选参数 -b 以Byte为单位显示内存使用情况. -k 以KB为单位显示内存使用情况. -m 以MB为单位显示内存使用情况. -g 以

python的内存分析和处理

这篇文章写得太好了,记录一下: 引自:https://www.jianshu.com/p/2f98dd213f04 2.内存分析和处理 程序的运行离不开对内存的操作,一个软件要运行,须将数据加载到内存中,通过CPU进行内存数据的读写,完 成数据的运算. 2.1 程序内存浅析 软件的程序在计算机中的执行,主要是通过数据单元.控制单元.执行单元共同协作,完成数据的交互达到程序处理数据的目的,在软件的执行过程中,由于系统内存和CPU的资源有限,所以有效的分解软件中的各项数据,将不同的数据加载到不同的内