question

simonx-0248 avatar image
0 Votes"
simonx-0248 asked simonx-0248 answered

Looking for advice on memory management with Visual C++

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?

windows-apic++vs-general
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

What memory errors? Can you be specific? Perhaps the program is not working as designed and all symptoms will be eliminated when the problem is solved.

0 Votes 0 ·
simonx-0248 avatar image
0 Votes"
simonx-0248 answered

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



5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

StriveSun-MSFT avatar image
0 Votes"
StriveSun-MSFT answered

Hello, @simonx-0248

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.


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

simonx-0248 avatar image
0 Votes"
simonx-0248 answered

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))?










5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

simonx-0248 avatar image
0 Votes"
simonx-0248 answered StriveSun-MSFT commented

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.

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi, please share a minimal reproducible code sample.

0 Votes 0 ·
simonx-0248 avatar image
0 Votes"
simonx-0248 answered

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

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

simonx-0248 avatar image
0 Votes"
simonx-0248 answered

Hi, please share a minimal reproducible code sample.

It may take me a while to do that...

Meanwhile, 2 further things:

(1) Calling Flush() on one of my RenderTargets (during Print processing) somehow stopped all subsequent screen paints working. I have no idea why. But that was definitely the cause. If I commented out that line (or skipped past in the debugger), screen painting continued to work fine. If I left it in, all screen paints stopped working immediately. For the time being, I have stopped calling 'Flush' altogether on any RenderTargets.

(2) I realised that during Print processing, I actually can easily access an ID3D11DeviceContext, because I call D3D11CreateDeviceAndSwapChain which will return one if asked to do so. I tried storing a pointer to one of these, and calling 'ClearState' and 'Flush' on it before generating each new page in the Print process. It may have worked. It's hard to tell because I still got memory errors (see next para). But I commented it all out just to be safe when I had the problem with Flush being called on a RenderTarget (even though the ID3D11DeviceContext calls were not directly implicated).

The thing is, that even if the above calls to ClearState and Flush work, as far as I can see they only at best result in clearing cached resources associated with that particular ID3D11DeviceContext - i.e. resources that I have used exclusively during Print processing itself. But most of the problem is clearing all the cached resources that I use for screen painting, which I want to do right at the start of Print processing. And although I can free the relevant bitmaps (ID2D1Bitmap), I can't get Direct2D (or Direct3D) to synchronously free up the associated resources, because for screen painting, I only have a ID2D1RenderTarget (or a ID2D1DeviceContext) and can't find any way of getting hold of its associated ID3D11DeviceContext (although I assume there must be one somewhere?). Can you suggest any way that I can do that? - i.e. get hold of the ID3D11DeviceContext that is associated with the screen paints and their ID2D1RenderTarget ? I have done a lot of searching and failed to find a way of doing that.

For interest, my application is an MFC application and it just uses DirectWrite and Direct2D most of the time. I get a CDCRenderTarget passed to me from the MFC (via a message function that uses registered message AFX_WM_DRAW2D), and get the render target from that.

P.S. I just tried getting a ID2D1Device from the ID2D1DeviceContext passed to my app by the MFC, and calling "ID2D1Device::ClearResources" on that. It doesn't throw any errors but it also makes no significant difference.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.