Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. running out of memory, issues with GC

running out of memory, issues with GC

Scheduled Pinned Locked Moved C#
c++questioncsharpasp-netdata-structures
4 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    Jon Hulatt
    wrote on last edited by
    #1

    My app is two parts: main executable is in C#, and the core of the app is mixed native C++ with C++/CLI. The native parts use ffmpeg's libavcodec family of api's to grab select frames from a video, do some processing on them, encode them as jpeg, and then return the encoded jpeg data as a (managed) byte[] array back to the main c# app. The app is fast, and makes a lot of allocations - there are maybe 5 frames of jpeg data returned to the main app per second, and each frame is 4.6 MB in size. The problem is that memory usage creeps up. Short runs of the program it doesn't matter, but longer runs and I run out of memory. The C++/CLI part of the app returns the data to the C# app simply by calling a callack funtion with a byte[] parameter. I'm sure there are no leaks in the native code part of the app; if i comment out the line that calls the callback then the app runs forever without leaking memory. I can therefore play with the code in the callback function - most of it is superficial and can be commented out. But the one line that seems to cause the problem is when I write the byte array to a (previously created) BinaryWriter (which is tied to a FileStream):-

    // the BinaryWriter is created like this
    m_outputFile = new FileStream(@"C:\filename.bin", FileMode.Create, FileAccess.Write);
    m_outputWriter = new BinaryWriter(m_outputFile);

    // the callback function can have everything stripped out so it looks like this
    public void ReturnFrame(byte[] frameData)
    {
    m_outputWriter.Write(frameData);
    }

    With the code like that, memory usage just keeps increasing until i run out of memory. If I comment out the m_outputWriter.Write(frameData) line, memory usage remains in check for the entire runtime of the program. There are no other references to frameData - the caller had the only other one, which is nulled asap, as shown here.

    void EncodeThread(Object^ o)
    {
    // irrelevant code removed here

    array<Byte>^ jpegData = gcnew array<Byte>(Width * Height * 3);
    Marshal::Copy(IntPtr(pRgbFrame->data[0]),jpegData,0,Width * Height * 3);
    m_callback->ReturnFrame(jpegData);
    jpegData = nullptr;

    // irrelevant code removed here
    }

    I've tried adding a GC.Collect() after the offending line, but it doesn't make any difference. I'm really confused as to what is going on here. The GC just doesn't seem to want to collect my garbage! Any ideas? Thanks Jon

    L 1 Reply Last reply
    0
    • J Jon Hulatt

      My app is two parts: main executable is in C#, and the core of the app is mixed native C++ with C++/CLI. The native parts use ffmpeg's libavcodec family of api's to grab select frames from a video, do some processing on them, encode them as jpeg, and then return the encoded jpeg data as a (managed) byte[] array back to the main c# app. The app is fast, and makes a lot of allocations - there are maybe 5 frames of jpeg data returned to the main app per second, and each frame is 4.6 MB in size. The problem is that memory usage creeps up. Short runs of the program it doesn't matter, but longer runs and I run out of memory. The C++/CLI part of the app returns the data to the C# app simply by calling a callack funtion with a byte[] parameter. I'm sure there are no leaks in the native code part of the app; if i comment out the line that calls the callback then the app runs forever without leaking memory. I can therefore play with the code in the callback function - most of it is superficial and can be commented out. But the one line that seems to cause the problem is when I write the byte array to a (previously created) BinaryWriter (which is tied to a FileStream):-

      // the BinaryWriter is created like this
      m_outputFile = new FileStream(@"C:\filename.bin", FileMode.Create, FileAccess.Write);
      m_outputWriter = new BinaryWriter(m_outputFile);

      // the callback function can have everything stripped out so it looks like this
      public void ReturnFrame(byte[] frameData)
      {
      m_outputWriter.Write(frameData);
      }

      With the code like that, memory usage just keeps increasing until i run out of memory. If I comment out the m_outputWriter.Write(frameData) line, memory usage remains in check for the entire runtime of the program. There are no other references to frameData - the caller had the only other one, which is nulled asap, as shown here.

      void EncodeThread(Object^ o)
      {
      // irrelevant code removed here

      array<Byte>^ jpegData = gcnew array<Byte>(Width * Height * 3);
      Marshal::Copy(IntPtr(pRgbFrame->data[0]),jpegData,0,Width * Height * 3);
      m_callback->ReturnFrame(jpegData);
      jpegData = nullptr;

      // irrelevant code removed here
      }

      I've tried adding a GC.Collect() after the offending line, but it doesn't make any difference. I'm really confused as to what is going on here. The GC just doesn't seem to want to collect my garbage! Any ideas? Thanks Jon

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      Hi, several questions: 1. how are you setting the callback? 2. what is (or would be) the prototype of the callback in native C++? 3. who is allocating and freeing the (native?) buffer that the callback is assumed to consume? 4. don't you close the file (m_outputWriter and m_outputFile)? without it, the stream grows bigger and bigger. :)

      Luc Pattyn [Forum Guidelines] [My Articles]


      The quality and detail of your question reflects on the effectiveness of the help you are likely to get. Show formatted code inside PRE tags, and give clear symptoms when describing a problem.


      J 1 Reply Last reply
      0
      • L Luc Pattyn

        Hi, several questions: 1. how are you setting the callback? 2. what is (or would be) the prototype of the callback in native C++? 3. who is allocating and freeing the (native?) buffer that the callback is assumed to consume? 4. don't you close the file (m_outputWriter and m_outputFile)? without it, the stream grows bigger and bigger. :)

        Luc Pattyn [Forum Guidelines] [My Articles]


        The quality and detail of your question reflects on the effectiveness of the help you are likely to get. Show formatted code inside PRE tags, and give clear symptoms when describing a problem.


        J Offline
        J Offline
        Jon Hulatt
        wrote on last edited by
        #3

        Luc Pattyn wrote:

        1. how are you setting the callback?

        My main form in the c# app implements an interface, ICaptureCallback, which looks like this:-

        interface ICaptureCallback
        {
        // other stuff removed
        void ReturnFrame(byte[] frameData);
        }

        The C++/CLI class that calls this takes an ICaptureCallback parameter in it's contstructor.

        Luc Pattyn wrote:

        2. what is (or would be) the prototype of the callback in native C++?

        Not sure why this is relevant; the callback is implemented solely in managed code.

        Luc Pattyn wrote:

        3. who is allocating and freeing the (native?) buffer that the callback is assumed to consume?

        The data originates in a native buffer from ffmpeg, but these lines of code:-

        array<Byte>^ jpegData = gcnew array<Byte>(Width * Height * 3);
        Marshal::Copy(IntPtr(pRgbFrame->data[0]),jpegData,0,Width * Height * 3);
        m_callback->ReturnFrame(jpegData);
        jpegData = nullptr;

        which are from the C++/CLI class that does the call show that the data is Marshal.Copy'd into a managed byte array. Although I've not shown the code, I'm confident that the native buffer is properly freed.

        Luc Pattyn wrote:

        4. don't you close the file (m_outputWriter and m_outputFile)? without it, the stream grows bigger and bigger.

        Because the objective of this app is to store the frames in a big binary file. Other code, which I've removed from the callback stored the offsets and frame sizes for later use. To close the file would stop the exact designed functionality of the app. Note, that the file is a disk file, not a memory stream, so should only get big on the disk, not take up much ram. thanks for your interest. Jon

        using System.Beer;

        L 1 Reply Last reply
        0
        • J Jon Hulatt

          Luc Pattyn wrote:

          1. how are you setting the callback?

          My main form in the c# app implements an interface, ICaptureCallback, which looks like this:-

          interface ICaptureCallback
          {
          // other stuff removed
          void ReturnFrame(byte[] frameData);
          }

          The C++/CLI class that calls this takes an ICaptureCallback parameter in it's contstructor.

          Luc Pattyn wrote:

          2. what is (or would be) the prototype of the callback in native C++?

          Not sure why this is relevant; the callback is implemented solely in managed code.

          Luc Pattyn wrote:

          3. who is allocating and freeing the (native?) buffer that the callback is assumed to consume?

          The data originates in a native buffer from ffmpeg, but these lines of code:-

          array<Byte>^ jpegData = gcnew array<Byte>(Width * Height * 3);
          Marshal::Copy(IntPtr(pRgbFrame->data[0]),jpegData,0,Width * Height * 3);
          m_callback->ReturnFrame(jpegData);
          jpegData = nullptr;

          which are from the C++/CLI class that does the call show that the data is Marshal.Copy'd into a managed byte array. Although I've not shown the code, I'm confident that the native buffer is properly freed.

          Luc Pattyn wrote:

          4. don't you close the file (m_outputWriter and m_outputFile)? without it, the stream grows bigger and bigger.

          Because the objective of this app is to store the frames in a big binary file. Other code, which I've removed from the callback stored the offsets and frame sizes for later use. To close the file would stop the exact designed functionality of the app. Note, that the file is a disk file, not a memory stream, so should only get big on the disk, not take up much ram. thanks for your interest. Jon

          using System.Beer;

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          Hi Jon, I think I start to get the overall picture now. Here are some thoughts: 1. your buffers are "large", i.e. larger than 85KB, hence allocated on the large-object-heap LOH which never gets compacted, and therefore is subject to fragmentation (unless all objects have the exact same size). In extreme cases fragmentation could mean your app is really using only a few megabytes, yet fails to allocate one more megabyte. Remember: collections such as lists are implemented as arrays, not linked lists, so whenever their count exceeds the next power of two, the array gets reallocated with twice the size, potentially also invading the LOH. 2. The line jpegData = nullptr; probably is not relevant since in the previous line (m_callback->ReturnFrame(jpegData);) you passed the reference to another method, which could store it somehow, and hence keep the array alive. 3. You may have a performance problem: with 5 frames per second, you need an overall throughput of 23MB/sec which you copy once with Marshal.Copy, copy once more with m_outputWriter.Write, and finally the system must dump to the disk in order for the memory to become available again. That could be beyond your system's capabilities; watch Task Manager and/or experiment with less data (smaller frames, or fewer frames per second, maybe skip every second frame). 4. Maybe you could simplify things (and reduce the system load) by passing a ready-made buffer to your native code, rather than have it allocate, fill, copy and deallocate. That is how I have always done image processing in mixed environments. If all the data needs to go through is generate natively and write to a managed file, you could do that with just one array, using it over and over. Hope this helps. If you need more detailed discussions, I'd probably want to see ALL code relevant to allocating, pinning. copying and freeing buffers. :)

          Luc Pattyn [Forum Guidelines] [My Articles]


          The quality and detail of your question reflects on the effectiveness of the help you are likely to get. Show formatted code inside PRE tags, and give clear symptoms when describing a problem.


          1 Reply Last reply
          0
          Reply
          • Reply as topic
          Log in to reply
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes


          • Login

          • Don't have an account? Register

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • World
          • Users
          • Groups