Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Exiting gracefully when memory exhausted



  • My Easy Data Transform application can use a lot of memory and I want to be able to handle running out of memory gracefully. So I do this:

    bool Application::notify( QObject* receiver, QEvent* event )
    {
        bool done = true;
        try
        {
            done = QApplication::notify( receiver, event );
        }
        catch ( std::bad_alloc& ex )
        {
            releaseMemory();
            qWarning() << "bad_alloc exception: " << ex.what();
            m_outOfMemoryDlg->exec();
            exit( 1 );
        }
        catch ( const std::exception& ex )
        {
            releaseMemory();
            qWarning() << "exception: " << ex.what();
            criticalMessage( nullptr, QString( "%1 has to close due to an exception. Please contact support." ).arg( APP_NAME ) );
            exit( 2 );
        }
        catch ( ... )
        {
            releaseMemory();
            qWarning() << "exception";
            criticalMessage( nullptr, QString( "%1 has to close. Please contact support." ).arg( APP_NAME ) );
            exit( 3 );
        }
        return done;
    }
    

    Where m_outOfMemoryDlg is a QDialog with a helpful message I create and hide at startup. This all works fine on macOS. I get a std::bad_alloc exception, the QDialog appears and the program shuts gracefully. But on Windows the whole OS just locks. The screen is frozen and I have to power off and back on to make it responsive.

    In my .pro file I added:

    CONFIG += exceptions
    
    win32 {
        QMAKE_CXXFLAGS_EXCEPTIONS_ON = /EHa
        QMAKE_CXXFLAGS_STL_ON = /EHa
    }
    

    I also added:

    #ifdef Q_OS_WIN
    #include <new.h>
    LONG WINAPI
    exceptionFilter( struct _EXCEPTION_POINTERS* exceptionInfo )
    {
        if ( exceptionInfo )
        {
            EXCEPTION_RECORD* er = exceptionInfo->ExceptionRecord;
            if ( er )
            {
                qWarning() << "Windows exception: " << er->ExceptionCode;
            }
        }
        exit( 4 );
        return EXCEPTION_EXECUTE_HANDLER;
    }
    
    int outOfMemory( size_t )
    {
        qWarning() << "Out of memory";
        exit( 5 );
        return 0;
    }
    #endif
    
    Application::Application( int& argc, char** argv )
    : QApplication( argc, argv )
    {
    #ifdef Q_OS_WIN
        SetUnhandledExceptionFilter( exceptionFilter );
        _set_new_handler( outOfMemory );
    #endif
    ...
    }
    

    As per https://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus . Still no luck. Any ideas?



  • One way to avoid this is to query available memory before any big block memory is allocated.



  • @JoeCFD At best, that's just a race condition. Another app could allocate everything between when you query and when you try to allocate. There's no good definition of "available memory" on modern operating systems to even query.

    Best bet is just to allow the user to configure some limits and bail before you hit them. Trying to respond gracefully once your application has already had a failed allocation is almost certainly going to fail in some way or another. Better to fail safe than to try to plow ahead in an unknown state.



  • @wrosecrans Agree. Add some factor to the available memory while it may be hard to get the exact number of free memory.



  • @wrosecrans said in Exiting gracefully when memory exhausted:

    @JoeCFD At best, that's just a race condition. Another app could allocate everything between when you query and when you try to allocate. There's no good definition of "available memory" on modern operating systems to even query.

    Best bet is just to allow the user to configure some limits and bail before you hit them. Trying to respond gracefully once your application has already had a failed allocation is almost certainly going to fail in some way or another. Better to fail safe than to try to plow ahead in an unknown state.

    I have considered allowing the user to set a maximum memory usage and exit gracefully if they hit that limit. But the current appoach seems to work fine on macOS, I'm surprised it doesn't work on Windows.



  • @AndyBrice Don't be surprised if it starts to fail visibly on MacOS after an OS update, a new Qt version, or some changes to your code. It "appears" to work, but you can't rely on the behavior you are seeing.



  • @wrosecrans You might be right. I'm just surprised there doesn't seem to be a reliable way to handle this.

    I don't suppose Qt has a crossplatform way to find out how much memory a process is using? That would be useful.



  • I'm not aware of Qt providing anything that fits the bill.

    Providing a single useful memory usage number from an application's perspective sounds challenging. Shared pages, mapped but unallocated pages, buffers held on behalf of the process, etc make the analysis complicated.


  • Moderators

    @AndyBrice said in Exiting gracefully when memory exhausted:

    I'm surprised it doesn't work on Windows

    I‘m surpised as well, maybe its msvc‘s exception handling? I remember that to be bad or haphazardly implemented. Try with nothrow and check against nullptr instead.

    I guess you know how to override new/delete to use a custom memory pool ? I fyou decide to go the fixed max memory route



  • @J-Hilk

    Try with nothrow and check against nullptr instead.

    That would involve a lot of rewriting!

    I guess you know how to override new/delete to use a custom memory pool ?

    Sounds nasty...!



  • @jeremy_k The Windows or Mac task manager shows the amount of memory a process is consuming. That is the number I want.


  • Moderators

    @AndyBrice

    That would involve a lot of rewriting!

    you do not have to necessarily rewrite your whole code, a small test program could, as first step be enough, no ?

    Sounds nasty...!

    I've never done it myself, and only seen it done once, in the source code of Command & Conquer


Log in to reply