JIT--第一次--標記已--存根--調用--查找存根--執行機器碼 C#和CIL的關係: C#和N#都是CIL實現,但是彼此不能互通: C#和N#公開不分滿足規範,我們才能互通 CLS就是描述多語言互通的規範 記憶體分配:線程棧 堆Heap: 一個程式運行時,該進程存放引用類型變數的一塊記憶體,全局唯 ...
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 編譯器優化了,直接就是大山
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嗎?答案是,會的。
析構函數:被動清理;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"); } // }