`
黄继华
  • 浏览: 45070 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

对象的内存模型

 
阅读更多

操作系统和运行库通常将用于容纳数据的内存划分为两个独立的区域,每个区域都采取既然不同的方

式来进行管理,这两个区域通常称为堆栈和堆。

调用一个方法时,它的参数以及它的局部变量需要的内存从堆栈中获取,方法结束后(要么返回,要

么抛出异常),为参数和局部变量分配的内存将自动归还给堆栈并可在另一个方法调用时重新使用。

使用new关键字和一次构造函数来创建一个对象(类的实例)时,构建对象所需的内存总是从堆中获取

,使用引用变量,和一个对象可以从几个地方引用,对对象的最后一个引用消失后,对象使用的内存

就可供重用(虽然没有立即被回收)

所有堆类型都是在堆栈上创建的,所有引用类对象时都是在堆上创建的。堆和堆栈来源于运行库对内

存进行组织的方式。

堆栈内存就像一系列堆叠越高的箱子,调用方法时,每个参数都被放入一个箱子,并将这个箱子放到

堆栈最顶部,每个局域变量也同样分配一个箱子,并同样放到堆栈的最顶部,方法结束后,它的箱子

都会从堆栈中移除。

堆内存像散布在房间里的一大堆箱子,而不像堆栈那样每个箱子都严格地叠置在另一个箱子的上方,

每个箱子都有一个标签,标记箱子时候使用,创建一个对象时,运行库会查找一个空箱子,并把它分

配给对象,对象的引用存储在堆栈上的一个局部变量中,运行库将跟踪每一个箱子的引用数量(两个

变量有可能引用同一个对象),一旦最后一个引用消失,运行库就将箱子标记为未使用,将来的某个

时候,会消除箱子里的东西,使其真正能够重用。

void Method (int Param) //Param=42

{

Circle c=new Circle(Param);

}

堆栈中将分配出一小片内存(刚好一个int)

并使用42初始化在方法内部,还要从堆栈中分配处一小块内存,它刚好能存储一个引用,暂时不初始

化(为C准备),接着从堆中分配一块足够大的内存区域,来容纳Circle对象,堆内存是一种有限的资

源,如果堆内存被耗尽,new一个对象时会抛出异常,OutOfMemoryException,创建对象失败。

首先堆栈和堆(托管堆)都在进程的虚拟内存中。(在32位处理器上每个进程的虚拟内存为4GB

堆栈stack

堆栈中存储值类型。

堆栈实际上是向下填充,即由高内存地址指向地内存地址填充。

堆栈的工作方式是先分配内存的变量后释放(先进后出原则)。

堆栈中的变量是从下向上释放,这样就保证了堆栈中先进后出的规则不与变量的生命周期起冲突!

堆栈的性能非常高,但是对于所有的变量来说还不太灵活,而且变量的生命周期必须嵌套。

通常我们希望使用一种方法分配内存来存储数据,并且方法退出后很长一段时间内数据仍然可以使用。此时就要用到堆(托管堆)!

堆(托管堆)heap

堆(托管堆)存储引用类型。

此堆非彼堆,.NET中的堆由垃圾收集器自动管理。

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

比如创建一个对象:

Customer cus;

cus= new Customer();

申明一个Customer的引用cus,在堆栈上给这个引用分配存储空间。这仅仅只是一个引用,不是实际的Customer对象!

cus4个字节的空间,包含了存储Customer的引用地址。

接着分配堆上的内存以存储Customer对象的实例,假定Customer对象的实例是32字节,为了在堆上找到一个存储Customer对象的存储位置。

.NET运行库在堆中搜索第一个从未使用的,32字节的连续块存储Customer对象的实例!

然后把分配给Customer对象实例的地址赋给cus变量!

从这个例子中可以看出,建立对象引用的过程比建立值变量的过程复杂,且不能避免性能的降低!

实际上就是.NET运行库保存对的状态信息,在堆中添加新数据时,堆栈中的引用变量也要更新。性能上损失很多!

有种机制在分配变量内存的时候,不会受到堆栈的限制:把一个引用变量的值赋给一个相同类型的变量,那么这两个变量就引用同一个堆中的对象。

当一个应用变量出作用域时,它会从堆栈中删除。但引用对象的数据仍然保留在堆中,一直到程序结束 或者 该数据不被任何变量应用时,垃圾收集器会删除它。

1,什么是GC
GC
的全称是garbage collection,中文名称垃圾回收,是.net中对内存管理的一种功能。垃圾回收器跟踪并回收托管内存中分配的对象,定期执行垃圾回收以回收分配给没有有效引用的对象的内存。当使用可用内存不能满足内存请求时,GC会自动进行。在进行垃圾回收时,垃圾回收器回首先搜索内存中的托管对象,然后从托管代码中搜索被引用的对象并标记为有效,接着释放没有被标记为有效的对象并收回内存,最后整理内存将有效对象挪动到一起。这就是GC的四个步骤。
由上可见,GC是很影响性能的,所以一般说来这种事情况还是尽量少发生为好。
为了减少一些性能影响,.netGC支持对象老化,或者说分代的概念,代是对象在内存中相对存现时期的度量单位,对象的代数或存现时期说明对象所属的代。目前.net的垃圾回收器支持三代。每进行一次GC,没有被回收的对象就自动提升一代。较近创建的对象属于较新的代,比在应用程序生命周期中较早创建的对象的代数低。最近代中的对象位于零代中。每一次GC的时候,都首先回收零代中的对象,只有在较低代数的对象回收完成后仍不能满足需求的情况下才回收较高代数的对象。

2,读文章,看到了一个关于堆和栈(堆栈)的区别的比喻。很形象:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

3GC和堆栈、堆
由前述堆栈和堆的概念可以看出,堆栈不存在垃圾收集的问题,只需要直接压栈即可,而堆,则面临着很复杂的垃圾回收的问题。GC完全是对堆进行操作的,而对堆中对象是否有效的判断则是通过遍历堆栈来实现的。这里涉及到一个引用计数的概念,引用计数是对堆中对象被引用次数的统计,当一个对象的引用计数为零了,那么这个对象就可以被回收了。在进行GC的时候,垃圾回收器遍历堆栈,当发现一个堆地址的时候,它就将堆中该地址上的对象的引用计数加1,然后销毁堆中所有引用计数为零的对象,回收内存并整理堆中的碎片。

4,类实例化的步骤
类是最常见也是我们用的最多的一种引用类型,我们知道实例化一个类使用的是一个我们司空见惯的语句:
ClassA ca = new ClassA()

那么这短短的一句话中,计算机又做了些什么事情呢?
实际上,计算机在这个过程中大致做了这么几件事:
首先,在ClassA ca的时候,生成一个空的引用指针,并将它推入堆栈中:
然后,在new ClassA()的时候,生成ClassA的新的实例,并放入堆中:
在赋值号=这一步,将ca的引用指针指向刚刚生成的新实例:
这个时候,才算完成了整条语句的操作。

5,特例:string 类型与堆
大家知道,string类型是一种引用类型。但它又有一些值类型的特征。比如指向同一个字符串的两个string变量,如果其中一个变量值发生了改变,却不会影响到另外一个string变量。这就是因为:
(1)CLR
使用了一种叫字符串驻留的技术,对于
string str1="abc";
string str2="abc";
CLR初始化时,会创建一个内部的散列表,其中的键为字符串,值为指向托管堆中字符串的引用。刚开始,散列表为空,JIT编译器编译方法时,会在散列表中查找每一个文本常量字符串,首先会查找"abc"字符串,并且因为没有找到,编译器会在托管堆中构造一个新的指向"abc"String对象引用,然后将"abc"字符串和指向该对象的引用添加到散列表中。
  接着,在散列表中查找第二个"abc",这一次由于找到了该字符串,所以编译器不会执行任何操作,代码中再没有其它的文本常量字符串,编译器的任务完成,代码开始执行。执行时,CLR发现第一个语句需要一个"abc"字符串引用,于是,CLR会在内部的散列表中查找"abc",并且会找到,这样指向先前创建的String对象的引用就被保存在变量s1中,执行第二条语句时,CLR会再一次在散列表中查找"abc",并且仍然会找到,指向同一个String对象的引用会被保存在变量s2中,到此s1s2指向了同一个引用,所以System.Object.Equals(s1,s2)就会返回true了。
(2)
当使用重载操作符”=”string对象赋值时,string的对象是引用类型,它保留在堆上,而不是堆栈上.因此,当把一个字符串赋给另一个字符串时,会得到对内存中同一个字符串的两个引用.例如,修改其中一个字符串,就会创建一个全新的string对象(注意,这个过程发生在”=”),而另一个字符串没有改变.

6
C#中值类型分配在堆栈中。
值类型:bool,byte,char,decimal,double,enum,float,int,long,sbyte,short,struct,uint,ulong,ushort.
7
C#中引用类型分配在堆中,在堆栈中创建一个指向到堆的引用,返回给声明的变量。
引用类型:class,delegate,interface,object,string

备注:1. 值类型数组虽然分配在堆上,但数组元素依然是值类型,并没有被装箱。
2,
引用对象的值类型成员也随对象一起分配在堆上,同样也还是值类型,没有被装箱

分享到:
评论

相关推荐

    C++对象内存模型.pdf

    C++ 对象内存模型 C++ 对象内存模型是 C++ 编程语言中一个重要的概念, 它描述了 C++ 对象在内存中的存储结构。这个模型是 C++ 编程语言的基础之一,对于理解 C++ 编程语言的工作机理具有重要的意义。 在 C++ 中,...

    易语言对象内存模型详解

    易语言对象内存模型详解 易语言,作为中国本土开发的一款特色编程语言,旨在降低编程的难度,让普通用户也能进行程序设计。其独特的对象内存模型是理解易语言程序运行机制的关键部分。本文将深入探讨易语言对象内存...

    vector list map pair stl 标准模板库 c++

    - `reserve()`可以预先分配内存,避免因元素增加导致的频繁内存重新分配。 2. **list**: - `list`是由双向链表实现的容器,支持快速的插入和删除操作,特别是在容器的开头和结尾。 - `splice()`函数允许将一个`...

    一种用于类C语言环境的安全的类型化内存模型.pdf

    鉴于此,何炎祥等人提出了一种结合了字节级内存模型和面向对象内存模型的新型安全类型化内存模型。这种模型旨在结合两者的优势:能够描述低级内存操作(如指针算术、结构赋值、类型转换等),同时又提供足够的抽象...

    Objective-C的内存管理和对象复制

    本人查阅资料,以ppt的形式描述了iOS开发过程中可能困扰程序员的内存管理和对象复制等常见问题,以及iOS开发环境的升级带来的变化情况。 适合有入门基础的程序开发人员参考。

    深入理解Java内存模型 pdf 超清版

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了程序中各个线程如何访问和修改共享变量,以及如何确保数据的一致性。深入理解Java内存模型对于编写高效的并发程序至关重要。本文...

    JVM内存模型

    永久区内存(Permanent space)是JVM内存模型的重要组成部分,它存放了加载的Class类级对象,如class本身、method、field等等。永久区内存的大小可以通过调整PermSize参数来设置,如-XX:PermSize=256M -XX:...

    深度剖析java内存模型

    Java内存模型(Java Memory Model,简称JMM)是Java虚拟机(JVM)规范中定义的一种内存模型,它涉及了线程之间共享变量的可见性问题。在并发编程中,理解Java内存模型对于编写正确的多线程程序至关重要。 首先,...

    Java内存模型详解JMM.docx

    Java内存模型详解JMM Java内存模型(Java Memory Model,JMM)是Java虚拟机(JVM)中的一种内存模型,它描述了程序中各个变量之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节...

    深度探索C++对象模型.pdf

    C++对象模型是C++编程语言中一个核心且复杂的概念,它定义了如何在内存中表示类、对象以及它们之间的关系。理解C++对象模型对于深入掌握C++语言特性、优化代码性能以及进行高效内存管理至关重要。 #### 对象模型的...

    Java内存模型的历史变迁

    ### Java内存模型的历史变迁 #### 一、引言 随着多核处理器的普及与高性能计算需求的增长,Java作为主流编程语言之一,对于并发处理的支持变得越来越重要。Java内存模型(Java Memory Model,简称JMM)作为Java...

    Java 8 内存模型.pdf

    在详细介绍Java 8内存模型之前,需要了解的是,JVM(Java虚拟机)在启动时,操作系统会为JVM进程分配一系列内存区域,这些内存区域包括堆(Heap)、元空间(MetaSpace)、线程堆栈(Thread Stack)、共享库(Shared ...

    java内存模型文档

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了线程如何共享和访问内存,以及在并发编程中如何处理数据一致性的问题。理解JMM对于编写高效、线程安全的Java代码至关重要。 1. ...

    深度探索c++对象模型(2012版本)

    这本书的目标是帮助读者理解C++对象模型背后的细节,包括内存管理、类型系统、对象生命周期、继承、多态等核心概念。 C++对象模型是C++编程的基础,它描述了如何在内存中表示类和对象,以及它们之间的关系。首先,...

    Java内存模型详解

    ### Java内存模型详解 #### 1. JMM简介 ##### i. 内存模型概述 Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)的一部分,用于规定程序中的各种变量(包括实例字段、静态字段和数组元素等)在多个...

    JAVA内存模型与垃圾回收

    JAVA内存模型与垃圾回收是Java开发中至关重要的概念,它们直接影响到程序的性能和稳定性。首先,我们来看看Java内存模型。 Java内存模型,通常被称为JVM内存模型,它定义了程序中不同部分如何访问和共享数据。在...

    JVM内存结构、Java内存模型、Java对象模型1

    Java内存模型(JMM)与JVM内存结构不同,它是针对多线程环境下内存访问的抽象模型。JMM确保在多线程环境下,共享变量的读写操作具有正确的顺序和可见性,通过volatile、synchronized等关键字来实现这一目标。JMM关注...

    深度探索C++对象模型 PDF

    C++对象模型是C++编程的基础,它涉及到内存布局、虚函数、多态性、继承和封装等多个方面。首先,书中会详细讲解C++对象在内存中的表示方式,包括对象头、成员变量和成员函数的存储位置。理解这一部分对于优化内存...

Global site tag (gtag.js) - Google Analytics