前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >c#基础:值类型与引用类型2

c#基础:值类型与引用类型2

原创
作者头像
洪移潮
修改2024-11-27 16:38:29
修改2024-11-27 16:38:29
800
举报
文章被收录于专栏:C#从中级到高级C#从中级到高级

上一遍留下来一个题目:1.ToString() 有没有装箱,答案是没有

从以下几点来解释:

首先确认什么是值类型和引用类型:所有书和你说

值类型:放置在栈上

引用类型:放置在堆上

但是想下 List<int> 中的 值 到底存放在堆上 还是栈上,所以我这边拆分成5个点来梳理:

1. 单独使用场景 如 void a(){in i = 0; Object o = new Object()};

首先明确一点,所有的方法都是 以IL代码执行

所以首先将此IL代码加载到栈上,注意:是整个 IL 代码都直接 以方法的维度直接加载到栈上

其次 执行到 int i= 0; 会直接在栈上分配 4个字节32位来将 0 加载到 栈上,而不需要在堆上分配内存,当此方法使用完成,会直接丢失掉,除非又加载到 其他栈上,这个就是 栈上IL代码直接分配 基础类型.

而 Object 0 = new Object,需要在堆上分配内存,然后把地址加载到栈上,每次使用的时候,通过引用地址来操控.

也就是 去堆上分配地址,将地址返回到栈上,操作值的时候 先通过地址在堆上找到值,加载到栈上反反复复操作

简单说:

  1. 都是通过IL执行,
  2. 如果遇到基础类型,直接在栈上分配内存,即在哪里用在哪里分配,用完就扔
  3. 如果遇到对象类型,则先在堆上分配内存,将引用加载到栈上操作,用完还在堆上,等GC回收

2. 联合使用场景 如 void a(){in i = 0;List<i> list = new List<int>().Add(i)};

首先也是加载IL代码

其次正常执行基本类型在栈上分配

然后执行对象类型在堆上分配

最关键的点是 .Add(1) 中1 如何分配值的.

答案:在堆上分配连续的内存保存 1 的值, 那这时候 Add(1) 与1 有什么区别.

其实没区别,只不过保存的地方不一样.生命周期不一样,最终 在使用的点都是 加载到栈上 完全一致,

只不过在堆上是通过地址找到此值加载到栈上,

3. 联合使用场景 如

代码语言:txt
复制
void a(){
    List<i> list = new List<int>().Add(1);
    List<Object> list = new List<Object>().Add(new Object());
};

相同的点 就跳过.

堆上分配 1 与 new Object() 有什么不同?

多了几个步骤:

分配 1 就直接 分配4个字节即可

new Object() 首先 去找Object 类型,找到去加载,没找到 得先创造 Object 类型(注意 Object对象 和 Object 类型不一样,一个是 Object对象, 一个是 Type对象,名字是 Object),找到了 在堆上首先关联 Object类型对象信息,再去创造 同步块索引(这就是 lock 能够 出现锁的原因,全局唯一凭证)

总结:

1 和 Object 实质都是地址,只不过 1 只需要分配 4个字节即可, new Object() 首先去全局查 Object类型对象,没有还得全局加锁 毕竟类型对象也是全局加锁,在去全局创造同步块索引,在去分配内部空间。

所以 也就是因为 引用类型的巨大开销,为了复用,所以放置在堆上,而不是每次重新创造。

并不是 栈和堆的明显不同,也并不是 1和 Object 分配地址不同。只不过 Object 需要分配更多的数据 和 空间

4.装箱 Object obj = 1;

首先分配 object地址,然后计算 Object 类型对象信息, 再去创造同步块索引, 在去 分配4个字节保存 1

其他2个开销才是最大的开销,而不是 栈 和 堆 上速度不同导致的 结果

PS: 针对类型对象信息 和 同步块索引 也有优化

1. 同步块索引已经优化到 只有在使用的时候 (如lock)才去创造,而不是每次都去

2. 类型对象信息 只要加载就不会GC,全局持有

总结下来就是: 1. 如果是基本类型在使用的时候栈上直接分配,引用类型在堆上分配

2. 如果List<i> 在堆上使用基本类型 只需要分配地址即可,但是引用类型不仅需要分配地址,还得分配同步块索引,类型对象信息。

3.int a = 1; Object b = 1;中 a只需要4个字节长度, b 需要先分配4个字节,然后分配类型对象信息8个字节,同步块索引8位,并且类型对象信息和同步块索引如果创建的时候是全局单一的。

所以这就是装箱的巨大开销。但是拆箱其实没啥开销,就是在栈上分配地址将值加载进来即可

5 1.ToString是否装箱

上面沟通了,只有在堆上 且 分配了类型对象信息及同步块索引才是装箱

那 1.ToString()是否分配了类型对象信息。也就是指向了 Integate 类型对象信息?

答案 是无,

那怎么输出了 “1”?

原因是 调用了 IL底层代码,没有去堆上找其类型对象信息去调用

所以这就是大家疑惑的点,也是我以前疑惑的点。

就和 thinking in java说的,JAVA一切皆为对象,但是后面少了一句不属于JAVA的不算对象

所以总结下来就是:

一切希望通过类型对象信息找方法或者实现的 都需要装箱 如 Object i= 1;ValueType i=1; 1.equals(1);

6.问题c#中多态的实质是什么?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 4.装箱 Object obj = 1;
  • 5 1.ToString是否装箱
  • 6.问题c#中多态的实质是什么?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档