一、描述問題 當托管代碼調用非托管代碼的時候,經常會出現如下報錯:“嘗試讀取或寫入受保護的記憶體。這通常指示其他記憶體已損壞”。 二、原因分析 由於非托管代碼的記憶體指針的回收是由非托管代碼自身手動完成的,而不是像托管代碼一樣有統一的垃圾回收機制,比如.NET的GC。 所以對於托管代碼的調用方來說無法控制 ...
一、描述問題
當托管代碼調用非托管代碼的時候,經常會出現如下報錯:“嘗試讀取或寫入受保護的記憶體。這通常指示其他記憶體已損壞”。
二、原因分析
由於非托管代碼的記憶體指針的回收是由非托管代碼自身手動完成的,而不是像托管代碼一樣有統一的垃圾回收機制,比如.NET的GC。
所以對於托管代碼的調用方來說無法控制其記憶體回收。以上問題的產生原因很可能是托管代碼調用了已經被回收的非托管對象。封裝
的好一點的非托管代碼一般都會有記憶體釋放的介面供外部調用,這樣調用方就可以管理非托管代碼的記憶體回收。
三、解決方法
儘量不用使用不確定的非托管代碼裡面提供的對象。如果必須使用的話,實例化完成後直接使用而不要通過什麼方法傳遞,委托回調等方式來
獲取裡面的對象,在這些複雜未知的過程中,很可能非托管的對象已經被回收了。
四、示例說明
當使用C#調用OpenCVSharp的時候,經常會出現以上問題。
錯誤代碼如下:
1、訂閱滑鼠回調方法,並將mat的指針Data作為參數傳遞給回調方法。
1 private void button15_Click(object sender, EventArgs e) 2 { 3 Mat mat = GetMat();//獲取Mat對象 4 Cv2.SetMouseCallback("輸入圖像", GetRGBCvMouseCallback, mat.Data); 5 }
2、回調方法,獲取每個點的RGB值
1 private void GetRGBCallbackMethod(MouseEvent @event, int x, int y, MouseEvent flags, IntPtr userdata) 2 { 3 switch (@event) 4 { 5 case MouseEvent.LButtonDown: 6 7 using (Mat mat = new Mat(Rows, Cols, _MatType, userdata))//這種方式會被記憶體回收,直接在這裡面獲取對象 8 { 9 for (int i = 0; i < Rows; i++) 10 { 11 for (int j = 0; j < Cols; j++) 12 { 13 Vec3b s = mat.At<Vec3b>(j, i);//獲取第i0行第i1列) 這個方法會死 嘗試讀取收保護記憶體 14 } 15 16 } 17 } 18 break; 19 default: 20 break; 21 }
運行報錯,截圖如下:
正確代碼:
將7行代碼Mat mat = new Mat(Rows, Cols, _MatType, userdata)改成using (Mat mat=GetMat()) ,就不會出現以上問題。
原因分析:
原因很可能是userData中回調的過程中已經被回收了,當再次使用時就會報嘗試訪問被保護的記憶體的錯誤。為了
防止此問題的發生,解決辦法就是重新實例化一個Mat對象,不使用可能被回收的userData數據。