堆和栈
堆空间
堆空间是程序中一块独立的空间,
从堆空间分配的数据可以被程序中的所有函数和线程访问,
并且不会随函数返回与线程结束释放。
栈空间
每个线程都有独立的栈空间,栈空间用于保存调用函数的数据
释放堆内存的时机由 .net runtime 决定,大多时候我们不需要关心
内存泄漏主要指堆空间的泄露
值类型和引用类型
值类型:分配在栈中,本身储存着值
引用类型:
包括两部分:指向内存的地址的地址 和 值本身(堆中)
(class中的 值类型 在堆中)
值 类 型 的 对 象 会 根 据 定 义 的 位 置 隐 式 分 配 与 释 放 。
引 用 类 型 的 对 象 需 要 通 过n e w关 键 字 显 示 分 配 ,n e w会 从 堆 空 间 申 请 一
块 空 间 用 于 保 存 值 , 然 后 返 回 空 间 的 开 始 地 址 。
new关键字本质:调用构造函数,设置成员值
对引用类型进行赋值,内存中只会多出一块指向已分配空间的内存地址
如果内存中没有值指向某已分配空间,这个空间就会在一个合适的时机被GC回收
GC
针对 堆空间回收
主要工作:找出堆空间分配的空间中哪些空间不再被程序使用,然后回收这些空间。
.Net 实现GC的方式(最主流):”标记并清除“方式
根对象:包括
- 各个线程栈空间上的变量
- 全局变量
- GC句柄
- 析构队列中的对象
.Net 中托管代码分配的对象称为托管对象
分配托管对象使用的堆,称为托管堆
其他的堆空间,用于分配.Net运行时的对象
.Net GC机制
分代
0代、1代、2代
首先根据内存大小,小对象在第0代,大对象在第2代
在一轮GC后,第0代对象(还没被回收的)通常会成为第1代
0代 -> 1代
1代 -> 2代
2代 -> 2代
第0代中的对象存活时间通常最短,
第1代中的对象存活时间比较长,
第2代中的对象存活时间最长。
(通常来说,存活越长时间的对象会一直存在)
例外情况:
分代机制:
0、1、2代都处理的GC称为完整GC
分代依据的目的是:
尽量增加每次执行垃圾回收处理时,可回收的对象的数量,并减少处理所需的时间。
压缩
反复执行分配与回收操作,可能导致堆上产生很多空余空间,
这些空余空间又被称为碎片空间。
压缩机制可以通过移动已分配空间把碎片空间合并到一块,
使得堆可以分配更大的对象。
.NET运行时提供的GC是支持压缩机制的,但是只在一定 的条件下启用。
大小对象概念
(超过大约 85000 B的对象称为大对象)
.NET根据引用类型对象值占用的空间大小来区分是小对象还是大对象。
大对象与小对象会在不同的堆区域中分配:称为大对象堆和小对象堆。
移动大对象需要的(时间、资源)成本很高
(新分配的小对象归属0代、大对象归属2代)
前面提到的压缩机制,默认只在小对象堆启用,大对象堆是不会执行压缩的。