(二)学习C#之内存管理

一、当你运行你的程序通常都会访问哪些内存空间呢?

电脑自言自语道,“这个人要声明一个整数”或“这个人个方法”或“这个人要创建一个对象”

1.这些信息究竟是存在内存里的什么地方呢?

2.或者说用于描述这个整数、这些对象和方法的信息,都在哪呢?

3.这些内存的组织方式又是什么样的呢?

二、有三类不同的空间,但是其中两类与我们的关系最密切。

1.第一类存储空间:用于存储所有静态变量和常量

我们可以把它们想作不会变的变量,这是一块专门划分出来的空间,是一块比较特殊的空间,在这块特殊的空间存放着静态变量和常量,那么这特殊空间在何处呢,我们不是特别关心,总之它就是我们的特殊小可爱。

因为在程序开始运行的时候,这些东西就会被放置在内存中,我们的这些常量永远不会改变,这意味着他们并不需要被放在那些我们常常访问的内存中,他们只要呆在内存中的某个地方,然后我们知道如何访问它就可以了。

2.第二类存储空间:用于存储动态变量

1).什么是动态变量?

动态变量就是利用new申请到的变量,所以当你用new来申请一个对象的时候,比如new Goval(参数),这样就得到一个动态变量,动态变量对应的内存,也就是你用new申请到的叫做堆。

2).什么是堆?

堆就像是一大摞衣服,当你需要衣服的时候你过去说,“嘿,我需要些新衣服”然后它就给你些衣服,你穿完这些衣服就会说,“嘿,我再也不要穿这些衣服了”,然后神奇的事情就是,被你舍弃的衣服就变成了一块布,计算机知道你不再穿这些衣服了,它说,“我要把这些内存拿回来”,这就叫做垃圾回收。

垃圾回收的实质:当堆里的内存不再被需要的时候,就会被收回。

3.第三类存储空间:用于存储本地变量

这些本地变量存放在一个叫栈的地方

     什么是栈?

和堆的概念是不一样的。对栈来说,当你在某方法中访问本地变量时,或者说你在调用方法时要传递某些参数时,比如说这些参数,你实际上是把这些参数的值进行了复制,在内存中分配了另一块空间来存储它们,而时间上在栈中就会有一些空间会被自动分配给你,当你在访问本地变量后者是传递参数,或者是调用方法时,这些变量都是活跃的,而当这些变量不再活跃时,比如方法调用结束,或者遇到了右花括号时,这些变量就会消失,它们也就不能再会以任何形式被调用,然后栈就说到,“嘿,我要把这些内存空间拿回来”。

4.介绍静态变量、动态变量和本地变量

1)静态变量: 所有静态变量,或者叫特殊变量,都存储在内存中比较低的位置,也就是说,这些位置对应的数值都是比较小的,比如说在1000这个位置,注意这个1000是十六进制的而不是十进制的。

2)堆:当堆变大时它是在往下变大。往下变大是什么意思?这就是说当从2000开始的时候,你说,“嘿 堆 我需要给我的新GOval准备一些空间”它说,“好的 我给你在这里分配些内存,就在2004这个地方好啦”,然后你说,“我需要给我的新GWreck准备一些空间,它说“好的,2008这地方归你了,我会在这里这给分配空间”,所以说当堆越来越大的时候,我们就说堆在向下变大。

3)栈:栈从一些比较高的地址开始,从FFFF开始,当它分配内存时,它会向上变大。也就是说,这些地址数值会越来越小。还有一个参量用于描述本地变量、参数和动态变量,当然主要说的是动态变量。那就是它们所占用的空间大小。

4)不同变量实际上需要不同大小的存储空间,比如一个整数,int至少在C#,java占四个字节。动态变量还有一些附加内存开销,这实际上就是一些额外的内存空间,是整数和字符本身所占用空间外的一些空间,用来保存你的对象的其他信息。

三、在程序开始运行的时候,或者创建新对象的时候,到底发生了什么......

准备:

1.我们可以通过调用构造方法创建一个点,并告诉它x和y坐标,然后它就会把这个坐标保存下来,你就可以去移动这些点。

2.刚开始只是有这个类,内存尚未被分派,什么事还都没做。我们写个程序,创建点对象,并调用其对应的方法。运行程序,我们就可以知道在个过程中,计算机内存里到底发生了什么。

3.运行:

1)第一件事:调用run方法,当程序刚开始运行时run方法会被调用。

当一个方法被调用时,我们会创建这些本地变量和参数,但是run没有参数,所以不需要为参数安排空间,但它的确有两个本地变量P1和P2,但是还不包括任何信息,因为我的程序刚刚开始,还没运行到new Point这个地方。

2)第二件事:调用new便可以得到一个动态变量,这意味着它将被存储在堆里。

a.记录保存一个点的数据。构造方法的作用是将在计算机分配的空间填进数据(2,3)。

b.overhead里面是额外内存开销。

c.为什么在p1那里写1000呢?其实是我们给这个新的点分配好空间之后,该如何处理这个点呢?我们把这个点分配给P1,这里有个关键的知识点,就是计算机是怎么知道P1和它代表的所有信息都存储在那里呢?每个对象都有一个地址,实际上计算机在内部是通过对象所处的内存地址,来找到这些对象的。所以当我调用new的时候,就会在堆中申请一块空间。然后把这段空间赋值给p1,到底是什么东西被赋值了呢?实际上是把P1的内存地址进行了赋值,确切的讲是起始地址。通过这个起始地址可以找到P1包含的所有数据,这些数据就被存储在stack(1000)里,这里的1000是内存地址1000。

3)第三件事:新建一个新点,需要从堆那里要点内存。

堆说,“1008之前的地址已经分配出去了,那我就接着给你分配一块能放得下一个点的空间,用来保存你的实例变量。

然后计算机就说好,分配我一个空间,然后调用构造方法,将点的数据填入存储单元,再将起始地址100C赋值给p2。px、py是属于p1的实例变量,后来分配的px、py是属于p2的实例变量。

4)第四件事:神奇的事件。调用一个方法,例如调用p1.move。

a.当我们调用这个方法时,我们会有一些参数和本地变量。也就是说我们要在栈上分配内存才能进行方法的调用。所以当我们调用方法时所发生的就是,“嘿,p1我要把你移动一下偏移(10,11)这么多”。那再内存中发生了什么事呢?内存中发生的事情就是:我们会创建一个新的“栈帧”,当你调用一个方法的时候,调用方法的方法,或者说原先运行的方法就会被挂起。内部的情况是这样的,我们在栈上分配内存空间,所以我们管它叫栈帧,它存储了调用这个方法所需要的信息。什么信息,首先是方法的调用,额外内存开销,需要存储所有传进来的参数。

b.关于this,有一个隐藏参数,当我针对某特定对象调用方法后,我如何得知这个方法要调用的对象是哪个?当你对一个对象调用方法的时候,在所谓的内存开销之后,紧接着放在栈上的内容,实际上是指向这个对象的指针;我们把它叫做this指针。this是对象访问自己的一种方式。它需要知道自己在内存的某个地方。

c.很奇怪,为什么对象需要知道自己呆在什么地方呢?

因为当我们写一个方法的时候,我们并没有将这些方法跟已经创建的某些特定对象进行关联。如果我创建一个特定的对象(如p1),然后对其调用那个方法,那个方法得知道,“嘿,你让我改变px py值,你说的是那个px py值”,这个时候,this就起作用了。

this说:“我知道这个方法对应对象在内存中的位置。”这样无论你在程序里进行什么样的操作,都可以通过this指定你要访问的对象。

进行方法调用的时候,首先要知道这个方法所对应的那个特定的对象的地址。我们针对p1进行调用,p1放在1000这个位置。所以p1就是1000,指针this指的值就是1000.接下来逆序存放传入的参数。传入参数值,这些是传入参数的副本。

5)第五件事:现来看看调用move方法时会怎样?现在栈里面已经存储了move的本地变量,和对象对应的指针地址。

a.第五步执行的是px加10,但是是那个px呢?我们顺着this指针找到的那个px。

如何找?

b.去this存储的地址找。于是它到达1000位置那里,读取额外内存开销信息,它就知道px相对起始位置1000的位置。然后它继续说“嘿已经找到了,它的值是2,我在它上面加上10变成12”。

6)第六件事:很有趣的事情。当栈内变量不再活跃时,它们就会被删除。

a.这个时候计算机会自动跑过来说,“嘿,这块变量,这块内存,再也不会用到,我要把它收回。”所以栈上这部分的内存,就是与move方法相关的栈帧。这些就是调用move分配的内存。也就是说所有为move分配的内存被机器自动的收回。这就叫做"栈里弹出"。想象一下,当栈顶的东西用完后,把它从栈顶上弹出去,然后它就没有了。

b.如果再次调用move方法?相关的信息又会重新放在栈帧上,但执行过一次move之后就不需要了。

注意:构造方法和其他方法的区别,构造方法不用this指针。

时间: 2024-11-25 18:21:41

(二)学习C#之内存管理的相关文章

C++语言学习(二十)——自定义内存管理

C++语言学习(二十)--自定义内存管理 一.统计类对象中成员变量的访问次数 mutable是为了突破const函数的限制而设计的,mutable修饰的成员变量将永远处于可改变的状态.mutable成员变量破坏了只读对象的内部状态,而const成员函数保证只读对象的状态不变性,因此mutable成员变量无法保证只读对象状态的不变性. #include <iostream> using namespace std; class Test { public: Test():m_count(0) {

cocos2d-x学习之自动内存管理

一.自动内存管理 1)概述 C++语言默认是没有提供自动内存管理的.使用者需要自己分配,自己释放.在cocos2d-x里提供了一个自动内存管理的方案.主要是通过CCObject来提供的,用户只要继承了CCObject,就可以通过调用autorelease()来告诉系统进行自动内存管理. 一般用法就是:    CCLayer* pLayer = CreateLayer(s_nActionIdx);    pLayer->autorelease(); 2)自动内存管理的实现 自动内存管理的实现原理大

黑马程序员----内存管理之二《多对象的内存管理》

内存管理之二——<多对象的内存管理> 1.多对象的内存管理方式: 只要有人使用了这个对象,这个对象就不能被销毁: 只要你想使用这个对象,就让这个对象的引用计数器的值+1(让对象做一次retain操作): 当你不再使用这个对象,就让这个的对象的引用计数器的值-1(让对象做一次release操作): 谁alloc,谁就release: 谁retain,谁就release: 2.内存管理的代码规范: 只要调用了alloc必须有release/autorelease set方法的代码规范: 1.基本数

linux kernel学习笔记-5内存管理(转)

http://blog.sina.com.cn/s/blog_65373f1401019dtz.htmllinux kernel学习笔记-5 内存管理1. 相关的数据结构 相比用户空间而言,在内核中分配内存往往受到更多的限制,比如内核中很多情况下不能睡眠,此外处理内存分配失败也不像用户空间那么容易.内核使用了页和区两种数据结构来管理内存: 1.1 页 内核把物理页作为内存管理的基本单位.尽管CPU的最小可寻址单位通常为字(甚至字节),但是MMU(内存管理单元,管理内存并把虚拟地址转换为物理地址的

学习 Sql Server 内存管理之术语理解

在学习sql server 内存管理时,看到一些term:memory node,memory clerk,memory object,非常迷惑,在此将自己的理解记录下来,以便后续学习 1,NUMA架构和memory node 内存节点基于硬件 NUMA 创建,memory node是Numa node内的内存块,属于server物理内存的一部分.Memory Node的作用是使得内存的分配由Windows移交到SQL Server OS层面执行. 查看memory node select *

Linux System Programming 学习笔记(九) 内存管理

1. 进程地址空间 Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间 内存页是memory management unit (MMU) 可以管理的最小地址单元 机器的体系结构决定了内存页大小,32位系统通常是 4KB, 64位系统通常是 8KB 内存页分为 valid or invalid: A valid page is associated with an actual page of data,例如RAM或者磁盘上的文件 An invalid page is

Objective-C学习笔记_内存管理(二)

一.属性的内部实现原理 assign的属性内部实现 setter方法: // setter方法 @property (nonatomic, assign) NSString *name; - (void)setName:(NSString *)name { _name = name; } getter方法: // getter方法 - (NSString *)name { return _name; } 观察下面代码会出现什么问题? NSString *name = [[NSString all

UNIX再学习 -- 死磕内存管理

malloc/free简化实现:malloc 和 sbrk 关系:虚拟内存机制. 一个内存管理 C 语言部分讲,UNIX部分讲,Linux部分还讲,死磕到底!! 一.mallc/free简化实现 上篇文章已经讲解了动态内存分配/释放函数,参看:UNIX再学习 – 内存管理下面来讲一下,它的自定义函数实现,其中有三个部分: 1.内存控制块 内存控制块用于管理每次分配的内存块,记录该内存块的字节大小.忙闲状态,以及相关内存控制块的首地址. 代码如下所示 typedef struct mem_cont

计算机操作系统学习笔记_7_内存管理 --内存管理基础

h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { font-family: "微软雅黑"; font-size: 16pt; }h2.ctl { font-family: "AR PL UMing CN"; font-size: 16pt; }h1 { margin-bottom: 0.21cm; }h1.western { fon