.NET 类型(Types)的那些事

引言

您是.Net工程师?那 .NetFramework中的类型您知道有三大类吗?(除了引用类型和值类型,还有?)

引用类型一定在“堆”上,值类型一定在“栈”上?

那引用类型在内存中的布局细节您又知道多少了?

 

.Net Framework 中的Types分类

 

09fig01

C# type categorization. 带阴影的都是 C# 的内建类型关键字.

除了object and string(分别为System.Object System.String别名), 其他带阴影的都是简单的值类型.

下面是摘自《C#语言规范5.0》 –> 4.类型(page:77)

C# 语言的类型划分为两大类:值类型 (Value type) 和引用类型 (reference type)。

第三种类型是指针,只能用在不安全代码中。

引用类型和值类型在内存中如何分配的呢?

这一块我们将通过一小段代码来讲解,在讲解前让我们回顾下

引用类型 和值类型的赋值过程中在内存处理上的区别:

  • 把一个值类型a(定义如下int a=80;)赋给另外一个值类型b(int b;),即(b=a;)时,会把a的值拷贝一份给a,如下图;

value_type

  • 把一个引用类型a(定义如下Employee a=new employee();)赋给另外一个引用类型b(Employee b;),即(b=a;)时,会把a的地址(引用)拷贝一份给a,即他们指向同一个地址;

reference_type1

开始代码讲解,首先看代码如下:

Form myForm = new Form();
Size s = new Size (100, 100);          // struct = value type
Font f = new Font (“Arial”,10);        // class = reference type
myForm.Size = s;
myForm.Font = f;

注意代码中 myForm.Size中的Size和myForm.Font的Font是Form类型的属性(Property)不是类型(class type,代表某个类型)。

在.NetFramework中这样的使用方式极其普遍,初学者不要混淆了这两者。

讲解代码前给大家再提下知识点:

  • Size是Struct类型,当然就是值类型(ValueType)
  • Font是Class类型,当然就是引用类型(ReferenceType)

 

上面这段代码的内存中的分配,示意图:

image006

很清楚地看到

  • Size类型的s分配到了Stack上,而Front类型的f 和Form类型的myForm则分配在堆上。
  • 并且myForm的Font属性引用到了Font类型f。
  • myForm的Size属性有它自己的值(Width和Height),它是Size类型s的一个拷贝。

这里我们更清晰的看到了值类型和引用类型在值赋值过程中的区别

我们可以通过修改Font类型f的值,来修改myForm中的字体样式,但不能通过修改s来修改myFrom的Size。

引用类型的Object内存布局基础结构

image_thumb_2

上面这个图展示的结构是通过分析源码得出的:

  1. 首先ObjectHeader(在其所在的AppDomain中的那个Thread通过调用Monitor.Enter锁了这个对象)
  2. 接下来是Method Table 指针(该指针指向AppDomain中声明(定义)托管类型),如果程序集被加载到AppDomain neutral 中,那么所有的AppDomain中该类型实例的Method Table指针都一样。CLR 类型系统的该基础构建块在托管代码中都是可视的。(TypeHandle.Value 是一个IntPtr)
  3. 最后就是这部分就是该类型实例的值。

CLR object的这个实例对象的地址在垃圾回收时也有可能会发生变动。(具体参看GC中压缩过程)

 

\sscli20\clr\src\vm\object.h

//
// The generational GC requires that every object be at least 12 bytes
// in size.   
#define MIN_OBJECT_SIZE     (2*sizeof(BYTE*) + sizeof(ObjHeader))

A .NET object has basically this layout:

class Object
{protected:MethodTable*    m_pMethTab;};
class ObjHeader
{private:// !!! Notice: m_SyncBlockValue *MUST* be the last field in ObjHeader.DWORD  m_SyncBlockValue;      // the Index and the Bits
};
Platform最小实例大小(bytes)
x8612 bytes = 2*4+4
x64 24 bytes = 2*8+8

 

引用类型的Object内存布局代表性结构

 

普通对象

 

012302077246530

数组对象 - Array

012302218801605

字符串对象

012302322553069

 

Boxing,小心您的值类型不经意间被装箱

 

int a=1;
object b=a;

这段代码大家都知道会发生装箱,装箱后原来的值类型会有哪些变化?看下装箱和拆箱的步骤:

装箱:
对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。
第一步:新分配托管堆内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。
第二步:将值类型的实例字段拷贝到新分配的内存中。
第三步:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。
有人这样理解:如果将Int32装箱,返回的地址,指向的就是一个Int32。我认为也不是不能这样理解,但这确实又有问题,一来它不全面,二来指向Int32并没说出它的实质(在托管堆中)。
拆箱:
检查对象实例,确保它是给定值类型的一个装箱值。将该值从实例复制到值类型变量中。
有书上讲,拆箱只是获取引用对象中指向值类型部分的指针,而内容拷贝则是赋值语句之触发。我觉得这并不要紧。最关键的是检查对象实例的本质,拆箱和装箱的类型必需匹配,这一点上,在IL层上,看不出原理何在,我的猜测,或许是调用了类似GetType之类的方法来取出类型进行匹配(因为需要严格匹配)。

那么给你个自定义结构体,你还清楚什么情况会被装箱吗?

参考《防止装箱落实到底,只做一半也是失败》

附加

为方便大家查看源码,这里提供一个源码索引表

SSCLI文件索引

ItemSSCLI Path
AppDomain\sscli\clr\src\vm\appdomain.hpp
AppDomainStringLiteralMap\sscli\clr\src\vm\stringliteralmap.h
BaseDomain\sscli\clr\src\vm\appdomain.hpp
ClassLoader\sscli\clr\src\vm\clsload.hpp
EEClass\sscli\clr\src\vm\class.h
FieldDescs\sscli\clr\src\vm\field.h
GCHeap\sscli\clr\src\vm\gc.h
GlobalStringLiteralMap\sscli\clr\src\vm\stringliteralmap.h
HandleTable\sscli\clr\src\vm\handletable.h
InterfaceVTableMapMgr\sscli\clr\src\vm\appdomain.hpp
Large Object Heap\sscli\clr\src\vm\gc.h
LayoutKind\sscli\clr\src\bcl\system\runtime\interopservices\layoutkind.cs
LoaderHeaps\sscli\clr\src\inc\utilcode.h
MethodDescs\sscli\clr\src\vm\method.hpp
MethodTables\sscli\clr\src\vm\class.h
OBJECTREF\sscli\clr\src\vm\typehandle.h
SecurityContext\sscli\clr\src\vm\security.h
SecurityDescriptor\sscli\clr\src\vm\security.h
SharedDomain\sscli\clr\src\vm\appdomain.hpp
StructLayoutAttribute\sscli\clr\src\bcl\system\runtime\interopservices\attributes.cs
SyncTableEntry\sscli\clr\src\vm\syncblk.h
System namespace\sscli\clr\src\bcl\system
SystemDomain\sscli\clr\src\vm\appdomain.hpp
TypeHandle\sscli\clr\src\vm\typehandle.h

更多源码参考http://www.projky.com/dotnet

参考

 

The Truth About .NET Objects And Sharing Them Between AppDomains

Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing

Shared Source Common Language Infrastructure

[翻译经典文章]深入.NET Framework内部, 看看CLR如何创建运行时对象的

.NET对象的内存布局

托管堆与垃圾收集

C# 装箱和拆箱[整理]

mdsn 类型详解(Jit and Run)

转载于:https://www.cnblogs.com/HQFZ/p/4638794.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几种去除数组中重复元素的方法、数组去重

工作中遇到的一个问题&#xff0c;就是去除数组中重复的元素&#xff0c;记录一下几种有效的方法&#xff1a; 第一种思路&#xff1a;遍历要删除的数组arr, 把元素分别放入另一个数组tmp中&#xff0c;在判断该元素在arr中不存在才允许放入tmp中。 <!DOCTYPE html> <…

MongoDB学习使用

一、什么是MongoDB&#xff1f; MongoDB是一个高性能&#xff0c;开源&#xff0c;无模式的文档型数据库&#xff0c;是当前NoSql数据库中比较热门的一种。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式&#xff0c; NoSql&#xff0c;全称是 Not Only Sql,指的是…

域账号更改密码之后代理需要重新配置

在使用域账号的时候&#xff0c;如果需要配置账户和密码&#xff0c;那么最好记录下来&#xff0c;否则将来找不到就很尴尬了。 我遇到的问题是&#xff0c;因为在另外一台电脑配置了域账号&#xff0c;用来联网&#xff0c;提供网络给visual studio 1.Firefox 这个代理的账号…

wcf精通1-15

随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第一天 三种Binding让你KO80%的业务 转眼wcf技术已经出现很多年了&#xff0c;也在.net界混的风生水起&#xff0c;同时.net也是一个高度封装的框架&#xff0c;作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单…

python如何实现共享报表系统_使用python来实现报表自动化-阿里云开发者社区

xlwt 常用功能xlrd 常用功能xlutils 常用功能xlwt写Excel时公式的应用xlwt写入特定目录(路径设置)xlwt Python语言中&#xff0c;写入Excel文件的扩展工具。可以实现指定表单、指定单元格的写入。支持excel03版到excel2013版。使用时请确保已经安装python环境。百度百科xlrd Py…

去除inline-block元素间间距的N种方法

这篇文章发布于 2012年04月24日&#xff0c;星期二&#xff0c;22:38&#xff0c;归类于 css相关。 阅读 147771 次, 今日 52 次 by zhangxinxu from http://www.zhangxinxu.com 本文地址&#xff1a;http://www.zhangxinxu.com/wordpress/?p2357 一、现象描述 真正意义上的in…

Docker深入浅出2

Docker系统架构 Docker使用客户端-服务端&#xff08;c/s&#xff09;架构模式&#xff0c;使用远程api来管理和创建Docker容器。 docker容器通过Docker镜像来创建。 容器与镜像的关系类似于面向对象编程中的对象与类的关系 Docker面向对象容器对象镜像类加速器配置&#xff1a…

mysql安装包下载密码_MySQL解压包的安装与下载的图文教程

这篇文章主要为大家详细介绍了mysql解压包的安装基础教程&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下由于换了新电脑&#xff0c;所以的环境都要到新电脑去配置。突然发现mysql的配置忘了&#xff0c;然后百度又重新来一遍。特地写一篇文章记录一…

php 扒取网页数据

扒取方法 public function index(){$url http://www.dytt8.net/;// $url Public/txt/movies.txt;$content file_get_contents($url);$content iconv("gb2312", "utf-8//IGNORE",$content);$reg "|<div class\"co_content2\">(.*…

多维DP UVA 11552 Fewest Flop

题目传送门 1 /*2 题意&#xff1a;将子符串分成k组&#xff0c;每组的字符顺序任意&#xff0c;问改变后的字符串最少有多少块3 三维DP&#xff1a;可以知道&#xff0c;每一组的最少块是确定的&#xff0c;问题就在于组与组之间可能会合并块&#xff0c;总块数会-1。…

多表联合查询

关联数据库字典表的多表联合查询 inner join…on 自动连接 需要用到表的所有信息时&#xff0c;可以用以下两种方法 1) left join…on… 左连接 &#xff08;以左为准&#xff0c;右边没有NULL代替&#xff09; 2) right join…on… 右连接&#xff08;以右为准&#xff…

python elasticsearch update_使用python的elasticsearch部分更新

我有以下格式的elasticsearch文档。我需要部分更新“x”字段并在其中添加python dict。{"_index": "gdata34","_type": "gdat","_id": "328091-72341-118","_version": 1,"_score": 1,"…

32位与64位注册表

如果32位系统OFP的注册表路径是 HKEY_LOCAL_MACHINE\SOFTWARE\Bohemia Interactive\ 那么在64系统里就应该是 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Bohemia Interactive\ 多了一级Wow6432Node转载于:https://www.cnblogs.com/zhang-pengcheng/p/4712135.html

http 请求头和响应头

客户端发送请求过程带着的数据&#xff1a; 1.请求地址 2.请求方式 3.请求头 request headers 4.请求参数 https://www.juhe.cn/ 130.... 1a2b....pei 服务端响应给客户端的信息&#xff1a; 1.响应内容 2.响应报文/响应头部 response headers a 响应头 b 响应体 3.http状…

[算法]-排序算法之希尔排序

希尔排序算法思想 希尔排序的实质就是分组插入排序&#xff0c;该方法又称缩小增量排序.基本思想是&#xff1a;先将整个待排元素序列分割成若干个子序列&#xff08;由相隔某个“增量”的元素组成的&#xff09;分别进行直接插入排序&#xff0c;然后依次缩减增量再进行排序&a…

python tkinter button颜色变不了_tkinter多按钮颜色变化

我使用tkinter创建一个8x8按钮矩阵&#xff0c;当按下单个按钮时&#xff0c;它会添加到最终列表中(例如finalList((0,0)&#xff0c;(5,7)&#xff0c;(6,6)&#xff0c;…)&#xff0c;允许我快速创建8x8(x&#xff0c;y)坐标图像。我已经创建了一个带有按钮的窗口&#xff0…

应用spss可靠性分析软件

问卷调查的可靠性分析 一、概念&#xff1a; 信度是指依据測验工具所得到的结果的一致性或稳定性&#xff0c;反映被測特征真实程度的指标。一般而言&#xff0c;两次或两个測验的结果愈是一致。则误差愈小&#xff0c;所得的信度愈高&#xff0c;它具有下面特性&#xff1a;1、…

springmvc中的单例问题

1&#xff0c;springmvc实际上是基于一个叫做DispatcherServlet的servlet的。servlet按照以往的学习经验&#xff0c;他是单事例多线程的。 Servlet生命周期 1.装载Servlet。这项操作一般是动态执行的。然而&#xff0c;Server通常会提供一个管理的选项&#xff0c;用于在Serve…

设计模式 -- 亨元模式(FlyWeight Pattern)

用来尽可能减少内存使用量&#xff0c;适用于存在大量重复对象的场景&#xff0c;达到对象共享&#xff0c;避免创建过多对象的效果&#xff0c;提升性能&#xff0c;避免内存溢出。 定义&#xff1a; 使用共享对象有效支持大量细粒度对象。 适用场景&#xff1a; 系统中存在大…

python3.6使用mysql_Python之——Python3.6连接MySQL

只安装了Python是不能连接数据库的&#xff0c;还要安装Python连接MySQL的相关类库&#xff0c;Python2.7连接MySQL的类库很多&#xff0c;MySQL官方最新支持的Python为Python3.4.&#xff0c;如下图所示&#xff1a;那么&#xff0c;在Python3.6上如何实现连接MySQL的功能呢&a…