一.我在C#中要调用VC编写的DLL,核心算法都是在VC中写的,里面也涉及到大量的内存的分配和释放。但是在调用DLL里的函数时,有时可以调用通过,有时不能调用通过,我在VC中定位的,出问题的地方都是在分配内存后,进行内存拷贝的地方,如:double *Image = new double[width*height];
for(i=0; i<width*height; i++)
*(Image+i) = *(Src_Image+i); 我之前一直怕C#这边有内存泄露,导致这样的问题存在,所以我优化过很多次,对于非托管资源Pen,Font,Brush,Graphics,Bitmap(主要是Bitmap图像的释放),在不需要的时候都进行了释放。
就算VC中的DLL函数可以调用通过,如果连续运行几次,在C#这边申请一个比较大的数组比如 double类型,大小为2048*1536*9就会报出“内存不足”的错误,但是物理内存是肯定够用的,我定义了一些工具类用于存放调用DLL计算后得到的一些数据,用数组存储,但是对于数组,在C#中不能手工对其进行释放,我在《C#高级编程》中,看到它用实现IDispose接口和析构函数来加速托管资源和非托管资源的释放,代码如下: public class Graphics2DShow:IDisposable
{
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize();
} protected virtual void Dispose(bool disposing)
{
if(!isDisposed)
{
if(disposing)
{
//释放图片非托管资源
}
//释放托管资源
}
isDisposed = true;
} ~Graphics2DShow()
{
Dispose(false);
}
}这样在代码中调用对象的Dispose方法,就释放托管内存和非托管内存,传递Dispose(bool disposing)的参数表示是用析构函数调用还是IDispose的Dispose()调用,真正实现清理的是该函数,这样做可以保证所有清理代码都放在一个地方。GC.SuppressFinalize();告诉垃圾回收器这个类不再需要调用其析构函数了,因为Dispose()已经完成了所有需要的清理工作,所以析构函数不需要做任何工作了。我的问题就是Dispose(bool disposing)中释放托管资源的地方的代码,对于我的类中含有的数组这些资源,我还是没办法手工进行释放的啊?所以这些数组资源的释放该如何释放呢?二.对于软件中需要的一些模态窗口,因为使用ShowDialog方法显示的窗口的关闭,并没有对其进行释放,所以我在Form_Closing方法中加了Dispose方法,但是我用AQTime测过,这些模态窗口并没有释放掉,是不是和我将窗口作为参数传给下一个窗口有关?三.在定义类的字段成员变量后,我喜欢随即初始化,但是这样会存在有时候没用到该变量时,也对其进行了初始化,造成了少量内存的浪费,比如一个引用类型的数组,这在AQTime中也会反映出来,如果在其初次使用的时候对其初始化,我觉得不怎么方便容易出错,还不怎么正规。反正在这个项目上内存的问题弄得我很头疼,我能用的方法都用出来了,有些问题还是没办法解决。
for(i=0; i<width*height; i++)
*(Image+i) = *(Src_Image+i); 我之前一直怕C#这边有内存泄露,导致这样的问题存在,所以我优化过很多次,对于非托管资源Pen,Font,Brush,Graphics,Bitmap(主要是Bitmap图像的释放),在不需要的时候都进行了释放。
就算VC中的DLL函数可以调用通过,如果连续运行几次,在C#这边申请一个比较大的数组比如 double类型,大小为2048*1536*9就会报出“内存不足”的错误,但是物理内存是肯定够用的,我定义了一些工具类用于存放调用DLL计算后得到的一些数据,用数组存储,但是对于数组,在C#中不能手工对其进行释放,我在《C#高级编程》中,看到它用实现IDispose接口和析构函数来加速托管资源和非托管资源的释放,代码如下: public class Graphics2DShow:IDisposable
{
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize();
} protected virtual void Dispose(bool disposing)
{
if(!isDisposed)
{
if(disposing)
{
//释放图片非托管资源
}
//释放托管资源
}
isDisposed = true;
} ~Graphics2DShow()
{
Dispose(false);
}
}这样在代码中调用对象的Dispose方法,就释放托管内存和非托管内存,传递Dispose(bool disposing)的参数表示是用析构函数调用还是IDispose的Dispose()调用,真正实现清理的是该函数,这样做可以保证所有清理代码都放在一个地方。GC.SuppressFinalize();告诉垃圾回收器这个类不再需要调用其析构函数了,因为Dispose()已经完成了所有需要的清理工作,所以析构函数不需要做任何工作了。我的问题就是Dispose(bool disposing)中释放托管资源的地方的代码,对于我的类中含有的数组这些资源,我还是没办法手工进行释放的啊?所以这些数组资源的释放该如何释放呢?二.对于软件中需要的一些模态窗口,因为使用ShowDialog方法显示的窗口的关闭,并没有对其进行释放,所以我在Form_Closing方法中加了Dispose方法,但是我用AQTime测过,这些模态窗口并没有释放掉,是不是和我将窗口作为参数传给下一个窗口有关?三.在定义类的字段成员变量后,我喜欢随即初始化,但是这样会存在有时候没用到该变量时,也对其进行了初始化,造成了少量内存的浪费,比如一个引用类型的数组,这在AQTime中也会反映出来,如果在其初次使用的时候对其初始化,我觉得不怎么方便容易出错,还不怎么正规。反正在这个项目上内存的问题弄得我很头疼,我能用的方法都用出来了,有些问题还是没办法解决。
malloc free
new delete
CoTaskMemAlloc CoTaskMemFree内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemAlloc,其垃圾回收自动调用的是CoTaskMemFree,也就是说,如果非托管代码采用COM方式分配内存,可以不用显示释放内存,net帮你搞定!而其他两种,net不支持的方式,必须还是由非托管方来释放!也就是:你要做C++和C#两边都封装一个相应的释放函数!
malloc free
new delete
CoTaskMemAlloc CoTaskMemFree内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemAlloc,其垃圾回收自动调用的是CoTaskMemFree,也就是说,如果非托管代码采用COM方式分配内存,可以不用显示释放内存,net帮你搞定!而其他两种,net不支持的方式,必须还是由非托管方来释放!也就是:你要做C++和C#两边都封装一个相应的释放函数!
C++些内存越界什么的可能不报错 ,但是同样的C++代码如过用C#来调用,就肯定错
C++些内存越界什么的可能不报错 ,但是同样的C++代码如过用C#来调用,就肯定错
调用到C#后,用CLR管理回收,最重要的是把你的dll封装好,包括一切可能的内存垃圾都要进行判断和手动回收.
//当然过多的手动回收也可能导致程序出现不可预见的错误.
对于楼主的这个错误,我想说一点,你知道你申请了多大的内存空间吗?2048*1536*9*8(8表示每个long真的空间大小)/1024/1024 = 216M字节。
我记得我的老师以前跟我说过,IDE对数组一般默认的是只能非配2M空间。你再瞧瞧吧。如果这样我觉得你的程序有问题了。
这样的话,你可以参见这个
http://stackoverflow.com/questions/6395305/c-dll-interop-with-c-exposing-multidimensional-arrays-of-unknown-size-and-m这里介绍如何由托管代码方分配数组提供给非托管代码方让后者填充,然后将其拷贝到托管数组的操作,你要注意的是,托管堆和CRT堆是两回事,没可能让由托管堆分配的内存在CRT堆上释放,反之亦然。所以,如果非托管代码进行内存分配并且使用malloc/new, 它通常也必须同时开放可以释放堆内存的接口(也就是封装了free/delete的操作), 上面有同学说用CoTaskMemAlloc CoTaskMemFree,这个没错儿,但前提是必须该对象是垃圾收集的对象,比如IntPtr就不行,必须手动进行分配和释放。另外,从你贴出来的代码,似乎你们想让dll分配的内存由.net那里释放?
http://msdn.microsoft.com/en-us/library/hk9wyw21%28v=VS.71%29.aspxunmanaged interface
int TestArrayOfInts(int* pArray, int pSize);
managed interface
public static extern int TestArrayOfInts([In, Out] int[] array, int size );unmanaged interface
int TestRefArrayOfInts(int** ppArray, int* pSize);
managed interface
public static extern int TestRefArrayOfInts( ref IntPtr array,
ref int size );
* 本内容使用CSDN 小秘书回复 *
* 每天回帖即可得10分可用分! *
*****************************[/align]
这个应该是大对象分配的垃圾回收策略受限吧,因为大对象是在第2代才回收,而且不会施行紧缩操作,也就是说相当于一个pinned对象,所以使用若干大对象以后(在这里是数组),会使得托管堆内存碎片变多导致后来的对象分配失败,报OOM异常,如果需要大规模的内存分配的互操作,在设计上会采取反复利用该空间,也就是使用缓冲池的方式而不是每次都分配.另外,你的这个"GC第0代 256kb,第1代 2M ,第2代 10M"是什么意思能解释下吗?
2.VC端使用malloc/new进行内存分配,这个倒和上面的大数组没有关系,是其它的DLL导出函数,在VC端处理时用到了malloc进行内存分配,而调用这些函数(好几个函数)出错的地方都在他开辟一段内存,进行内存拷贝的地方,也就是类似double *Image = new double[width*height];
for(i=0; i<width*height; i++)
*(Image+i) = *(Src_Image+i); 的地方(我在VC编的DLL中进行了定位,找到出错的地方)。
3.你说的“使用若干大对象以后(在这里是数组),会使得托管堆内存碎片变多导致后来的对象分配失败,报OOM异常,如果需要大规模的内存分配的互操作,在设计上会采取反复利用该空间,也就是使用缓冲池的方式而不是每次都分配”。缓冲池分配我倒是在C++中看到过,但在C#中不知道能不能用,怎么用。
http://blogs.msdn.com/b/yunjin/archive/2004/01/27/63642.aspx
1. CLR via C#
2. 精通.net互操作
3. .net高级调试用调试器试试看是最好的, windbg+SOS!
{
private int initialPoolSize;
private int bufferSize; private Queue<double[]> m_FreeBuffers; //pool of buffers public BufferPool (int initialPoolSize,int bufferSize)
{
this.initialPoolSize = initialPoolSize;
this.bufferSize = bufferSize;
m_FreeBuffers = new Queue<double[]>(initialPoolSize);
for (int i = 0; i < initialPoolSize; i++)
{
m_FreeBuffers.Enqueue(new double[bufferSize]);
}
} public double[] Checkout() // check out a buffer
{
if (m_FreeBuffers.Count > 0)
{
lock (m_FreeBuffers)
{
if (m_FreeBuffers.Count > 0)
return (double[])m_FreeBuffers.Dequeue();
}
}
//return new double[BUFFER_SIZE]; //instead of creating new buffer,blocking waiting or refusing request may be better
return new double[bufferSize];
} public void Checkin(double[] buffer) //check in a buffer
{
lock (m_FreeBuffers)
{
m_FreeBuffers.Enqueue(buffer);
}
}
}
}class ResultUVBufferPool:BufferPool
{
private static ResultUVBufferPool theResultUVBufferPool = null; public static ResultUVBufferPool getResultUVBufferPool(int initialPoolSize, int bufferSize) //单例模式
{
if(null==theResultUVBufferPool)
{
theResultUVBufferPool = new ResultUVBufferPool(initialPoolSize, bufferSize);
}
return theResultUVBufferPool;
} private ResultUVBufferPool(int initialPoolSize, int bufferSize): base(initialPoolSize, bufferSize)
{ }
}对程序中需要用到的数组先开辟出一片内存,需要用的时候,Checkout,用完之后Checkin至缓冲池中,继而做到重复使用这一块内存。缓冲池用单例。