JIT--第一次--標記已--存根--調用--查找存根--執行機器碼
C#和CIL的關系:
C#和N#都是CIL實現,但是彼此不能互通:
C#和N#公開不分滿足規范,我們才能互通
CLS就是描述多語言互通的規范
內存分配:線程棧
堆Heap:
一個程序運行時,該進程存放引用類型變量的一塊內存,全局唯一!只有一個堆
棧Stack:
數據結構,先進后出,線程棧,一個線程存放變量的內存(一個線程有一個)
值類型分配在棧上,比如結構、枚舉、int等
引用類型分配在堆上,比如類、接口、委托等
引用類型
1、調用new的時候,就會去棧上開辟內存,創建實例。這就是為什么在構造函數中跨域使用this
2、把實例的引用傳遞給構造函數
3、執行構造函數
4、返回引用
裝箱拆箱
int i=3; obect obj=i;//裝箱 int k=(int)obj;//拆箱
引用類型在哪里?值類型在哪里?
值類型的值,會隨着對象的位置存儲。引用類型的值,一定在堆里面。值類型的長度是確定的,引用類型的長度是不確定的,只有堆才能放各種值。
下面有一個例子:
public class MyTest { private int x; public MyTest(int n) { this.x=n; } }
MyTest t=new MyTest(3);//引用類型
那么,t.x 這個3,是存放在哪里呢?是堆上還是棧上?
===.》出現在堆里面,因為值類型的屬性,會隨着對象的位置存儲
public struct ValuePoint// : System.ValueType 結構不能有父類,因為隱式繼承了ValueType { public int x; public ValuePoint(int x) { this.x = x; this.Text = "1234"; } public string Text;//堆還是棧? }
struct是值類型,但是里面的Text,是存放在堆還是棧?答案是,對立面,因為引用類型的值,一定出現在堆里面。
string字符串內存分配
string student = "bingle1"; string student2 = student; Console.WriteLine(student);//bingle1 Console.WriteLine(student2);//bingle1 student2 = "bingle2";//=new string(APP); Console.WriteLine(student);//bingle1 Console.WriteLine(student2);//bingle2 Console.ReadLine();
string student = "bingle1"; string student2 = "bingle2";//共享 student2 = "bingle1"; Console.WriteLine(object.ReferenceEquals(student, student2));//true
為什么是true?因為同一個變量,享元分配內存。為什么享元?節約內存。
student2 = "binglebingle";//等於重新開辟一塊內存叫“binglebingle” new String("binglebingle") Console.WriteLine(student);//bingle1
還是bingle1,為什么?因為字符串的不可變性。為什么字符串不可以變,開辟新內存不浪費嗎?因為在堆上是連續拜訪的,如果有變化,會導致其他變量全部移動,成本太高,還不如重新new一個。
string student3 = string.Format("bing{0}", "le"); Console.WriteLine(object.ReferenceEquals(student, student3));//false
為什么是false?沒有享元。分配地址,然后計算,才知道是"bingle"。
string student4 = "bing" + "le"; Console.WriteLine(object.ReferenceEquals(student, student4));//true //true 編譯器優化了,直接就是bingle
string halfStudent = "le"; string student5= "bing" + halfStudent; Console.WriteLine(object.ReferenceEquals(student, student5)); //false 也是先內存,再計算
東西放在站上速度快,但是值類型是不能繼承的,長度也有限。
垃圾回收---CLR提供GC,托管堆垃圾回收
1、什么樣的對象需要垃圾回收?
托管資源+引用類型。線程棧的是不需要垃圾回收的,用完立馬就回收了。
2、托管資源和非托管資源
托管資源的就是CLR控制的,new的對象、string字符串。非托管就不是CLR控制的,數據庫連接、文件流、句柄、打印機連接。using(SqlConnection)//被C#封裝了管道了那個非托管的數據庫連接資源。只要手動釋放的,都是非托管的。
3、哪些對象的內存,能被GC回收?
對象訪問不到了,那就可以被回收了。程序----入口----去找對象---建立對象圖----訪問不到的就是垃圾,就可以回收了。
4、對象是如何分配在堆上的?
連續分配在堆上的,每次分配就先檢查空間夠不夠。
5、什么時候執行GC?
a、new對象的時候----臨界點
b、GC.Collection(),這個方法會強制GC
c、程序退出時會GC
a="123"
a=null
GC.Collect 可以GC,但是頻繁GC是不好的,GC是全局的
項目中有6個小時才運行new一次,什么時候GC? 不GC,可以手動GC
6、GC的過程是怎么樣的?
N個對象,全部標機Wie垃圾,入口開始遍歷,訪問到的就標機可以訪問(+1),遍歷完就清理內存,產生不連續的內存,壓縮,地址移動,修改變量指向,所以全局會阻塞。
清理內存分兩種情況:
a、無析構函數,直接清理內存
b、把對象轉到一個單獨的隊列,會有一個析構函數專門做這個。通常在析構函數內部是用來做非托管資源釋放,因為CLR肯定調用,所以避免使用者忘記的氣礦。
7、垃圾回收策略
對象分代:3代
0代:第一次分配到堆,就是0代
1代:經歷了一次GC,還存在的
2代:經歷了兩次或以上的GC,還存在的。
垃圾回收時,優先回收0代,提升小路,最多也最容器釋放。0代不夠,找1代,1代不夠找2代,再不夠就不用了。。。代的數值越大,越難回收。
大對象堆:一是內存移動大對象;二是0代空間問題。80000字節就叫大對象,沒有分代,直接都是2代。
那么,靜態資源在程序退出的時候,會GC嗎?答案是,會的。(其實回收的不是變量,是某個對象所占據的內存,如果存在一個對象,指向它的引用變量的數量為0,那個GC會擇機回收它占據的內存。應用程序域卸載的時候回收靜態變量。)
析構函數:被動清理;Dispose:主動清理
public class StandardDispose : IDisposable { //演示創建一個非托管資源 private string _UnmanageResource = "未被托管的資源"; //演示創建一個托管資源 private string _ManageResource = "托管的資源"; private bool _disposed = false; /// <summary> /// 實現IDisposable中的Dispose方法 /// </summary> public void Dispose() { this.Dispose(true); //必須為true GC.SuppressFinalize(this);//通知垃圾回收機制不再調用終結器(析構器) } /// <summary> /// 不是必要的,提供一個Close方法僅僅是為了更符合其他語言(如C++)的規范 /// </summary> public void Close() { this.Dispose(); } /// <summary> /// 必須,以備程序員忘記了顯式調用Dispose方法 /// </summary> ~StandardDispose() { //必須為false this.Dispose(false); } /// <summary> /// 非密封類修飾用protected virtual /// 密封類修飾用private /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (this._disposed)//已經被釋放的還可以不異常 { return; } if (disposing) { // 清理托管資源 if (this._ManageResource != null) { //Dispose this._ManageResource = null; } } // 清理非托管資源 if (this._UnmanageResource != null) { //Dispose conn.Dispose() this._UnmanageResource = null; } //讓類型知道自己已經被釋放 this._disposed = true; } public void PublicMethod() { if (this._disposed) { throw new ObjectDisposedException("StandardDispose", "StandardDispose is disposed"); } // }