使用GC手柄用GC.Collect()固定对象

时间:2014-02-06 12:14:12

标签: c# garbage-collection

环境: .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()

1 个答案:

答案 0 :(得分:4)

都不是。如果你发布内存,你最好希望GC.Collect()做一些有意义的事情。这两种方法都没有。如果它需要去任何地方,非常不清楚为什么,那么它应该进入close()方法。将 byteArray 设置为null。

这段代码很危险,很容易忘记调用close()并严重泄漏内存。这解释了为什么你认为你需要编写这段代码。它不会解决这个问题,需要一次性模式。终结器可以保证您不会忘记取消固定数组,Dispose()方法可以帮助您使用使用语句成功。

CopyMemory()调用也很危险,没有检查ImageSize&lt; = size并且没有检查图像格式。 1024 * 768不足以存储1024 x 768位图,一个像素通常需要3或4个字节。堆损坏很难调试。始终支持Marshal.Copy(),它不会允许破坏GC堆。另外一个优点是它不需要固定阵列。