Looking for advice on memory management with Visual C++

simonx 126 Reputation points
2021-03-22T18:37:41.07+00:00

Hi - my Visual C++ (Visual Studio 2019) MFC application (unmanaged code, not .net) produces reports of unlimited size - plus you can have multiple reports open at the same time. Reports can include an unlimited number of pictures. Users frequently create very large reports which can include large numbers of images, sometimes including full-page images (usually jpegs). My application uses DirectWrite and Direct2D for reports. For best performance, I cache images in memory where I can (hold onto ID2D1Bitmap objects). Looking at Task Manager, it seems (perhaps not surprisingly) that it is cached images that use a lot of memory. If I get a memory error, when handling images (e.g. when loading an image from a file), I free up memory throughout my application (release reference counts on ID2D1Bitmap objects) and try again. This strategy doesn't seem to work that well. For some reason, if I do this, I still tend to get memory errors after freeing the memory, whereas if I don't cache at all in the first place, things usually work better. I did debate calling EmptyWorkingSet after freeing up memory, but have heard that it's not advisable to do that...?

My application is 32-bit. In testing, memory errors mainly start to happen when memory usage hits about 1.4Gb.

Can anyone suggest a better strategy for managing memory - one that doesn't involve major changes to my code? Yes in time I will convert to 64-bit but I don't want to do that right now.

In principle I would like to stop caching long before I hit memory errors. Can anyone suggest a suitable mechanism/approach for doing that?

Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
4,594 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,418 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,526 questions
{count} votes

6 answers

Sort by: Most helpful
  1. simonx 126 Reputation points
    2021-03-23T09:54:36.67+00:00

    HRESULT hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, NULL, ppBitmap);
    if (hr == E_OUTOFMEMORY)
    {
    .... do something....
    }

    0 comments No comments

  2. Strive Sun-MSFT 426 Reputation points
    2021-03-29T09:14:35.847+00:00

    Hello, @simonx

    My application is 32-bit. In testing, memory errors mainly start to happen when memory usage hits about 1.4Gb.

    The total memory address that a 32-bit program can access is 4G. By default, 2G of memory is allocated to kernel mode, and the maximum memory that can be used in user mode is 2G. In fact, an application can only manage 1.6 – 1.7GB of memory at most. (in addition to the consumption of the program itself, the memory used by the user code is actually smaller.)

    It's not hard to understand why E_OUTOFMEMORY will appear.

    I still tend to get memory errors after freeing the memory, whereas if I don't cache at all in the first place, things usually work better.

    Release does not release immediately, calling either devicecontext Flush method or some swapchain present (which will flush the device) will cause the resource deletion.

    ID3D11DeviceContext :: Flush describes more details,

    Microsoft Direct3D 11 defers the destruction of objects. Therefore, an application can't rely upon objects immediately being destroyed. By calling Flush, you destroy any objects whose destruction was deferred. If an application requires synchronous destruction of an object, we recommend that the application release all its references, call ID3D11DeviceContext::ClearState, and then call Flush.

    Similar questions:

    ----------

    Thank you!

    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  3. simonx 126 Reputation points
    2021-03-30T20:29:19.417+00:00

    Thank you for that reply, but what you describe is not what i'm seeing. I have tried calling Flush() with the ID2D1RenderTarget object and it doesn't seem to make any difference. Subsequent calls to hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, props, ppBitmap); still fail in the same way, with an E_OUTOFMEMORY error. I want to be able to free up memory and then retry the same call, but once it has failed with E_OUTOFMEMORY that's what I always get, whatever I do to free up memory.

    The documentation may say that calling 'Flush()' will result in synchronous deletion, but that is not what I'm seeing. It looks to me as if resources are always and only cleaned up in Idle time. For example, if the user elects to print to a PDF file, the steps I execute are like this:

    (a) Free up all resources and call Flush on RenderTargets
    (b) Put up a dialog to get user to enter file name to use for PDF
    (c) Print to the PDF (and if I get any memory errors during the process, repeat step (a) and retry the call that failed)

    If I check the memory usage of my application, it stays high until (c) has completed, however long I wait before specifying a file name in step (b). If I cancel in step (b), memory usage will drop - but only after the dialog has closed. Otherwise, memory usage is shown as high throughout steps (a), (b) and (c), and drops only after all 3 steps have completed.

    If there really is no way to get Direct2D to free up resources synchronously, this seems to me a serious bug which should be fixed - bearing in mind that all Direct2D users are strongly urged to cache resources for better performance.

    Incidentally, I did try calling EmptyWorkingSet() to see if that would solve it. Weirdly it does have the effect that the memory usage of my process shown by TaskManager appears to drop immediately to almost nothing. But I still get memory errors if I retry the call that originally had the memory error immediately after calling EmptyWorkingSet (i.e. without waiting for Idle time processing).

    Finally, you suggest calling ID3D11DeviceContext::ClearState. But I don't have an ID3D11DeviceContext. Right now, I don't even have a ID2D1DeviceContext because I'm trying to support Windows 7. I only have a ID2D1RenderTarget and that doesn't have a 'ClearState' method. Unless you can tell me a way of getting one. But even if I could, would I want to call 'ClearState' if I'm in the middle of generating a print (which I might be - i.e. in the middle of step (c))?

    0 comments No comments

  4. simonx 126 Reputation points
    2021-03-31T14:02:51.357+00:00

    Just to be clear, although I cache bitmaps etc ordinarily for screen prints etc, and these are the caches that are freed up in step (a), I don't cache anything in step (c). In step (c) I create bitmaps, use them and immediately release them. But it seems like the resources are not freed at that point. Also I'm building up a print job and adding pages to a print control, and presumably there's accumulated memory use in that. The main body of memory that I should be able to free up is the memory used in step (a), and it seems crazy that I can't find any way of freeing it synchronously. It's as though I need to provide 2 buttons for users: a 'Free Memory' button and a 'Print' button, and tell them to press both buttons in that order. This would probably work (because the memory would be free up in Idle time following the first button press), but it's crazy UI and I'm not going to do it.

    So in summary: how can I free up memory allocated using Direct2D synchronously? StriveSun-MSFT? Anyone?

    All help much appreciated.


  5. simonx 126 Reputation points
    2021-03-31T14:04:18.957+00:00

    typo: "The main body of memory that I should be able to free up is the memory released in step (a)"

    0 comments No comments