环境: .NET4.0,基于Winform的应用程序,Windows 7(x64),VS2010。
以下是Test类的代码片段,它使用GC句柄通过GC.Collet()固定对象。 我有2个方法,如“Method1”和“Method2”。我想知道哪一个正确用于GC.Collect(),而GC句柄用于固定对象。
代码段:
class Test()
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
private GCHandle pinnedArray;
public GCHandle PinnedArray
{
get { lock (this) { return pinnedArray; } }
set { lock (this) { pinnedArray = value; } }
}
IntPtr pointer;
public IntPtr Pointer
{
get { lock (this) { return pointer; } }
set { lock (this) { pointer = value; } }
}
public instance()
{
const int size = 1024 * 768;
byte[] byteArray = null;
byteArray = new byte[size];
GCHandle PinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr Pointer = PinnedArray.AddrOfPinnedObject();
}
public void Method1(ref IntPtr ImageBuffer )
{
try
{
unsafe
{
if (ImageBuffer != IntPtr.Zero)
{
if (Pointer != null)
{
CopyMemory(Pointer, ImageBuffer, ImageSize);
// do stuff
//
m_numAcqs++;
if (m_numAcqs > _numMaxAcqs)
{
GC.Collect();
m_numAcqs = 0;
}
}
}
}
}
catch (Exception ex)
{
log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace));
}
return;
}
public void Method2(ref IntPtr ImageBuffer )
{
try
{
unsafe
{
if (ImageBuffer != IntPtr.Zero)
{
if (Pointer != null)
{
CopyMemory(Pointer, ImageBuffer, ImageSize);
// do stuff
//
}
}
}
m_numAcqs++;
if (m_numAcqs > _numMaxAcqs)
{
GC.Collect();
m_numAcqs = 0;
}
}
catch (Exception ex)
{
log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace));
}
return;
}
public close()
{
PinnedArray.free();
}
}
已编辑问题: 对不起,问题不明确。我附上了我的问题的真实代码。我有System.AccessViolationException错误。我有用于处理新图像的回调方法。这是代码片段。建议使用Marshal.Copy方法。但是,如果我使用这种方法,系统很慢。
// Local callback function used for handle new images
void HandleImage(ref Jai_FactoryWrapper.ImageInfo ImageInfo)
{
// Jai_FactoryWrapper.EFactoryError error = Jai_FactoryWrapper.EFactoryError.Success;
// This is in fact a callback, so we would need to handle the data as fast as possible and the frame buffer
// we get as a parameter will be recycled when we terminate.
// This leaves us with two choises:
// 1) Get the work we need to do done ASAP and return
// 2) Make a copy of the image data and process this afterwards
//
// We have the access to the buffer directly via the ImageInfo.ImageBuffer variable
//
// We can access the raw frame buffer bytes if we use "unsafe" code and pointer
// To do this we need to set the "Allow unsafe code" in the project properties and then access the data like:
//
// unsafe
// {
// // Cast IntPtr to pointer to byte
// byte* pArray = (byte*)ImageInfo.ImageBuffer;
// // Do something with the data
// // Read values
// byte value = pArray[10];
// // Write values
// for (int i = 0; i < 1000; i++)
// pArray[i] = (byte)(i % 255);
// }
// If we want to copy the data instead we can do like this without Unsafe code:
byte[] array = null;
if (ImageInfo.ImageBuffer != IntPtr.Zero)
{
// Allocate byte array that can contain the copy of data
array = new byte[ImageInfo.ImageSize];
IntPtr memoryDest = Marshal.AllocHGlobal((int)ImageInfo.ImageSize);
// Do the copying
Marshal.Copy(ImageInfo.ImageBuffer, array, 0, (int)ImageInfo.ImageSize);
Marshal.Copy(array, 0, memoryDest , (int)ImageInfo.ImageSize);
// Do something with the raw data
CopyToCogBuffer(memoryDest);
}
return;
}
private void CopyToCogBuffer(IntPtr pointer)
{
try
{
ICogImage8RootBuffer CogBuffer = new CogImage8Root();
CogBuffer.Initialize(int.Parse(myWidthNode.Value.ToString()), int.Parse(myHeightNode.Value.ToString()), pointer, int.Parse(myWidthNode.Value.ToString()), null);
if (CogBuffer != null)
{
CogImage8Grey pImage1 = new CogImage8Grey();
CogImage8Grey pImage2 = new CogImage8Grey();
pImage1.SetRoot(CogBuffer);
pImage2 = pImage1.Copy(CogImageCopyModeConstants.CopyPixels);
PImage = pImage2;
_numLiveDisplay++;
if (_numLiveDisplay > _numMaxLiveDisplay)
{
if (_liveProcessing)
{
if (CogDisplay != null)
CogDisplay.Image = (CogImage8Grey)pImage2;
m_numAcqs++;
if (m_numAcqs > _numMaxAcqs)
{
GC.Collect();
m_numAcqs = 0;
}
}
_numLiveDisplay = 0;
}
}
}
catch (CogException ex)
{
log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace));
}
catch (Exception ex)
{
log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace));
}
}
这样我就更改为固定对象了。如果我使用Kernel32.dll的“CopyMemory”方法,速度非常快。但是,随机发生了“System.AccessViolationException”。我想知道GCHandle PinnedArray是否正确使用。
private void HandleImage(ref Jai_FactoryWrapper.ImageInfo ImageInfo)
{
try
{
if (recipe.CameraInfo[indexID].Use)
{
byte[] byteArray = null;
if (ImageInfo.ImageBuffer != IntPtr.Zero)
{
// Allocate byte array that can contain the copy of data
byteArray = new byte[ImageInfo.ImageSize];
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
CopyMemory(pointer, ImageInfo.ImageBuffer, ImageInfo.ImageSize);
CopyToCogBuffer(pointer);
pinnedArray.Free();
}
}
}
catch (Exception ex)
{
log.AddErrorLog(this.Name, MethodBase.GetCurrentMethod().Name, string.Format("Error Occured : MESASGE[{0}]\r\nSOURCE[{1}]\r\nTRACE[{2}]", ex.Message, ex.Source, ex.StackTrace));
}
return;
}
以下是来自EventViewer的调用堆栈信息。
Application Program : ImageDelegateSample.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack :
at: Cognex.VisionPro.CogImage8Grey.Copy(Cognex.VisionPro.CogImageCopyModeConstants)
at: ImageDelegateSample.ClientManager.CopyToCogBuffer(IntPtr)
at: ImageDelegateSample.ClientManager.HandleImage(ImageInfo ByRef)
at: Jai_FactoryDotNET.CCamera.HandleImage(ImageInfo ByRef)
at: Jai_FactoryDotNET.CCamera+StreamWork.StreamThread()
at: System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at: System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at: System.Threading.ThreadHelper.ThreadStart()
答案 0 :(得分:4)
都不是。如果你发布内存,你最好希望GC.Collect()做一些有意义的事情。这两种方法都没有。如果它需要去任何地方,非常不清楚为什么,那么它应该进入close()方法。将 byteArray 设置为null。
这段代码很危险,很容易忘记调用close()并严重泄漏内存。这解释了为什么你认为你需要编写这段代码。它不会解决这个问题,需要一次性模式。终结器可以保证您不会忘记取消固定数组,Dispose()方法可以帮助您使用使用语句成功。
CopyMemory()调用也很危险,没有检查ImageSize&lt; = size并且没有检查图像格式。 1024 * 768不足以存储1024 x 768位图,一个像素通常需要3或4个字节。堆损坏很难调试。始终支持Marshal.Copy(),它不会允许破坏GC堆。另外一个优点是它不需要固定阵列。