当前位置: 首页 > news >正文

C#运行时的相互关系

C#运行时的相互关系

本博客主要讲述运行时类型、对象、线程栈和托管堆之间的相互关系,静态方法、实例方法和虚方法的区别,以及内存的分配和回收。

线程栈:在一个进程中可能包含多个线程,一个线程在创建的时候,会分配到一个大小1MB大小的栈,栈用于存储方法的实参、形参以及方法内部的局部变量,栈是从高位内存地址向地位地址构建的,由于栈有先进后出的特点,所以先定义的变量后被回收。

下面来看一个简单的例子,让你更了解线程栈

由于线程栈是从高位开始分配内存,先分配的我就画在上面了,在调用F1();方法时,分配内存的顺序是:name->n->F2的返回地址->Age->name;回收内存的顺序当然是反过来的。在一个方法中,应该包含一些序幕代码,进行一些初始化工作,还有一些尾声代码,等方法执行完成之后做一些回收工作。由于方法的返回地址先分配,在方法执行完成的时候回到返回地址,递归太深就容易出现栈溢出,请看我的《递归再一次让哥震惊了》,因为参数、局部变量都必须等到方法返回的时候才能回收。

在介绍托管堆之前先看看两个简单的类:

复制代码
    public class Person
{
private int height;
public void SetHeight(int height)
{
this.height = height;
}
public virtual void Say(string word) { }
public static string Head()
{
return "my head";
}
public static int Age = 100;
}
public class Student : Person
{
public override void Say(string word)
{
Console.WriteLine(word);
}
}
复制代码

复制代码
static void Main(string[] args)
{
Person student = new Student();
student.Say("Hello cth");
student.SetHeight(172);
Person.Head();
Console.ReadLine();
}
复制代码

 

CLR会在第一次访问一个对象时加载该对象,在这里,定义变量student时会为Person对象在线程栈中分配内存,第一次加载吗,在构造一个Student对象之前先要加载Student对象,并为Student类型对象分配内存,并构建一个Student对象。对象的地址存入线程栈中的局部变量student 中,我们知道类型对象的内容包含:类型对象指针、同步索引块、静态字段和方法(静态的和非静态的),不管是类型对象、还是实例类型都必须有类型对象指针、同步索引块;我们知道静态字段属于类,被这个类的所有实例共享,当然静态字段的内存是在类型本身中分配的,方法也是类的所有实例共享的,他的内存也是在类型本身中分配的,在每一个类型对象中都有一个方法表,类中定义的方法都有一个对应的项。

在构造一个对象的实例时,只需要为类型对象指针、同步索引块、该对象的实例字段分配内存,对于对象实例来说,类型对象指针可以让实例访问类型对象中德静态字段、方法等。

Student是线程栈中的定义的一个局部变量,保存Student的一个实例的在托管堆中的地址,所以他可以访问Student对象中的字段,方法,其实访问方法是通过类型对象指针访问类型对象Student中的方法表中对象的项。

Say方法的执行过程:变量student指向的是一个Student对象,调用的当然是Student类型对象中的Say方法,尽管在定义student的时候是Person类型,因为他是引用类型,他指向的是托管堆中Student对象的内存,然后遍历该对象的方法表,找到该方法调用。

特别说明虚方法,JIT在虚方法中加了一些额外的代码,方法每次调用的时候都会执行这些代码,这些代码会检查发出调用的变量,然后根据这个变量找到其应用的对象,然后调用这个对象的方法,若没有这些代码,你觉得CLR是调用父类的方法还是调用之类的方法呢,虚方法带来方便的同时,也多了这些必须的检查的代码。

SetHeight方法的执行过程:和Say方法前面是一样,只是在遍历Student对象的方法表时没有找到该方法,我们知道父类中定义的非private方法都可以被子类继承,是因为每个类型都定义了一个字段引用了他的基类,如果一个类调用的方法那个方法不是自己定义的,那么编译器会回溯类层次结构,一直到基类Object,找到相关的方法并调用,如果没有找到相关的方法就报了异常呗。所以SetHeight方法其实调用的是Person中的SetHeight方法。

Head方法的执行:由于Head方法是静态方法和上面两个方法有所不同,调用静态方法的时候,CLR会定位与静态方法对象的类型对象,然后在对应实例对象对象的方法表中查找相关的记录项,如果没有找到,同样会回溯。

当执行完student.SetHeight(172);时,student在也没有被引用,成为垃圾,在其所在的方法返回之前将会被回收,也就是说student实例对象被回收,释放其所在的内存,而类型对象不会被回收,类型对象的生成周期是:对象被加载到CLR中,直到其所在的AppDomain卸载。静态字段是他所引用类型的跟,所以被静态类型引用的对象永远不会被回收,如果其引用的是一个集合对象,并向其中不断的加入元素的话,就会造成内存泄露,更多关于内存管理垃圾回收,请看我的另一篇博客《垃圾回收--代》

 

作者:陈太汉

博客:http://www.cnblogs.com/hlxs/


本文转自啊汉博客园博客,原文链接:http://www.cnblogs.com/hlxs/archive/2011/12/26/2302592.html

相关文章:

  • ubifs笔记
  • OSI七层模型@year12
  • TopCoder - TCHS SRM 1 答案 及 代码
  • 洛谷 P1004 方格取数 【多线程DP/四维DP/】
  • 悟道一位IT高管20年的职场心经笔记
  • Flask入门的第一个项目进阶版
  • debian安装配置fcitx
  • 浅谈MySQL中的查询优化
  • 转载构造函数声明为Private和Protected
  • linux下Sublime的安装与插件推荐
  • 【X-Pack解读】阿里云Elasticsearch X-Pack Graph组件功能详解
  • PHP正则表达式的修饰符
  • HQL的select new map ···语法
  • 使用canvas检测HTML5视频解码错误
  • Android Studio 导出 Jar 给 Unity 使用
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Android 架构优化~MVP 架构改造
  • CSS相对定位
  • css属性的继承、初识值、计算值、当前值、应用值
  • Github访问慢解决办法
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Laravel 实践之路: 数据库迁移与数据填充
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • MaxCompute访问TableStore(OTS) 数据
  • Mysql优化
  • nodejs:开发并发布一个nodejs包
  • python 装饰器(一)
  • SpringBoot 实战 (三) | 配置文件详解
  • Vue 2.3、2.4 知识点小结
  • 翻译--Thinking in React
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 每天一个设计模式之命令模式
  • 你不可错过的前端面试题(一)
  • 算法---两个栈实现一个队列
  • 微信公众号开发小记——5.python微信红包
  • ###C语言程序设计-----C语言学习(6)#
  • #NOIP 2014# day.1 T2 联合权值
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)计算机毕业设计ssm电影分享网站
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)用.Net的File控件上传文件的解决方案
  • .naturalWidth 和naturalHeight属性,
  • .net 设置默认首页
  • .NET建议使用的大小写命名原则
  • .NET企业级应用架构设计系列之应用服务器
  • .NET使用存储过程实现对数据库的增删改查
  • .Net中的集合
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @Transactional类内部访问失效原因详解
  • [ C++ ] template 模板进阶 (特化,分离编译)