using System;
using System.Collections.Generic;
using System.Text;
namespace CLRCore
{
public class GCDemo
{
public static void Show()
{
//1.什么是GC?
// CLR提供的垃圾资源回收器
//2.什么是垃圾资源?
// 1.首先是托管资源,其次是引用类型-->引用类型的托管资源。
// 2.其次是程序访问不到的对象
// 程序--入口--去找对象--建立对象图--访问不到的就是垃圾
//3.为什么是引用类型的才被回收?
// 值类型都存放在栈(线程栈)上,随着线程的生命周期结束而结束
// 引用类型放在堆上,堆是全局唯一的,所以需要清理垃圾对象
// 引用类型在堆上是连续分配的,每次分配前都要检查空间够不够分配
//4.什么是托管/非托管资源?
// 托管资源,CLR管理的资源,new的对象,各种变量
// 非托管资源,CLR不能控制的,如数据库链接,打印机连接,文件流,句柄;
// 非托管资源都是要手动释放的
//5.为什么很多非托管资源也像托管资源一样用?using()
// 其实这些非托管资源的释放被封装了,当using结束时自动调用dispose方法
//6.什么时候执行GC
// a)new一个对象时达到临界点时
// b)GC.Collect 强制GC
// c)程序退出时
// 频繁GC不好,GC是全局的,会全局阻塞
// 一般内存过大就检查下静态属性,字段,这些占用的对象不会回收
//7.回收的过程是怎么样的?
// 全部对象标记为垃圾--从入口开始遍历所有对象--能访问到的标记可以访问(代+1)
// --清理内存,产生不连续的内存--地址移动,变量的地址变了--所以会全局阻塞
// 清理内存分两种情况:a)无析构函数,直接清理内存;b)有析构函数的,把对象转移到
// 一个队列,由专门的析构器线程清理(速度慢)。一般在析构函数
// 内释放非托管资源,析构函数由GC调用,不怕程序员忘记
//8 垃圾回收策略,分代策略
// 对象分代
// 0代:第一次分配到堆就是0代
// 1代:经历一次GC还在
// 2代:经历两次或以上
// 垃圾回收时优先0代,提升回收效率,0代最多也最容易
// 0代不够找1代--不够在找2代
//8 大对象堆,大对象堆策略
// 大于80000字节就是大对象,不分代,都是2代
// 避免大对象的移动
//9 析构函数(被动清理)和Dispose()主动清理
StandardDispose standardDispose = new StandardDispose();
standardDispose.PublicMathod();
standardDispose.Dispose();
standardDispose.PublicMathod();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace CLRCore
{
public class StandardDispose : IDisposable
{
//模拟非托管
private string _UnmanageResourse = "未被托管的资源";
//模拟托管
private string _ManageResourse = "托管的资源";
//标记是否被释放
private bool _disposed = false;
protected virtual void Dispose(bool disposedManage)
{
//表示可以被多次释放
if (_disposed)
{
return;
}
//清理非托管资源
if (_UnmanageResourse != null)
_UnmanageResourse = null;
if (disposedManage)
{
//清理托管资源
if (_ManageResourse != null)
_ManageResourse = null;
}
//标记已经被释放
this._disposed = true;
}
public void Dispose()
{
this.Dispose(true);//必须是true,GC才会回收
GC.SuppressFinalize(this);//通知垃圾回收机制不再调用终结器(析构器)
}
/// <summary>
/// 不是必要的,提供一个Close方法是为了符合其他语言的规范(C++)
/// </summary>
public void Close()
{
this.Dispose();
}
/// <summary>
/// 必须,以备程序员忘记了显示调用Dispose方法
/// </summary>
~StandardDispose()
{
//必须是false
this.Dispose(false);
}
public void PublicMathod()
{
if (_disposed)
{
Console.WriteLine("该对象已经被释放了。。");
return;
}
else
{
Console.WriteLine("PublicMathod。。");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CLRCore
{
public class StackHeap
{
//堆栈内存分配
//堆:一个进程运行时,引用类型存放的内存空间,全局唯一
//栈:先进后出的数据结构,也叫线程栈,一个线程存放值类型的内存空间,随着线程的生命周期
//值类型分配在栈上:结构 枚举
//引用类型分配在堆上:类 接口 委托
public static void Show()
{
//所有结构默认继承System.ValueType,结构不能有父类,因为隐式继承这个System.ValueType
{
ValuePoint point;// = new ValuePoint();//如果x是属性,必须= new ValuePoint();因为属性必须赋值
point.x = 123;//x必须赋值后才能使用
Console.WriteLine(point.x);
ValuePoint point1 = new ValuePoint();//默认一定有无参构造函数
}
{
ReferencePoint point1 = new ReferencePoint(123);
//1 new的时候就在栈上开辟内存,创建实例
//2 把实例的引用传递给构造函数
//3 执行构造函数
//4 返回引用
//值类型的值随着对象而存储
//引用类型一定出现在堆上
//因为值类型长度是确定的,引用类型的长度是不定的,也只有堆上能放不定长度的值
Console.WriteLine(point1.x);
}
//装箱拆箱
{
int i = 3;
object oi = i;//装箱,值类型出现在堆上了
i = (int)oi;//拆箱,堆上的值出现到栈上了
}
{
string str1 = "小王";
string str2 = "小高";
str2 = "小王";
Console.WriteLine(object.ReferenceEquals(str1, str2));//true
str2 = "大王";
//字符串在重新赋值时等于重新开辟一块内存放值,原值不变
//字符串的不可变性
//为啥不可变?开辟新内存这不是浪费吗?
//因为堆上的值的内存都是连续摆放的,节省空间嘛。这样的话一个值如果变化,会导致其他变量全部移动,成本太高,还不如新开内存
//StringBuild string.Format new String() 这几个字符串相同的情况下,引用也是不同的
Console.WriteLine(str1);
str2 = new String("小王");
Console.WriteLine(object.ReferenceEquals(str1, str2));//false
}
{
StackTrace stackTrace = new StackTrace();
//获取哪个类来调用的
Type type = stackTrace.GetFrame(1).GetMethod().DeclaringType;
//获取哪个类的哪个方法来调用的
string method = stackTrace.GetFrame(1).GetMethod().ToString();
Console.WriteLine(method);
}
}
}
public struct ValuePoint //所有结构默认继承System.ValueType,结构不能有父类,因为隐式继承这个
{
public int x;
public ValuePoint(int x)
{
this.x = x;
}
}
public class ReferencePoint
{
public int x;
public ReferencePoint(int x)
{
this.x = x;
}
}
}