qtexceptiontry-catchout-of-memoryqstringlist

Why can't I catch the OOM exception?


I have a function that reads a large file to fill a QStringList. The program crashes probably because there is not enough memory because if I use a small file the program runs well. I try to debug the problem by catching the exception.

QStringList readlargefile(QString filename)
{
    QStringList result;
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug()<<"cannot open file: "<<filename;
        return result;
    }
    QTextStream in(&file);
    in.setCodec("UTF-8");
    QString line;

    while(in.readLineInto(&line))
    {
        if(!line.isEmpty())
            result<<line;
    }

    file.close();
    return result;
}


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QStringList result;
    try
    {
        qDebug()<<"reading file";
        result=readlargefile("largefile.txt");
    }
    catch(...) {
        qFatal("got exception");
    }
}

The output is:

reading file
Killed

I cannot catch the exception, why?


Solution

  • If your program is aborted by OS it will not generate any exception. But you can setup a signal handler:

    void signalHandler(int)
    {
        //...
    }
    
    int main(int argc, char* argv[])
    {
        signal(SIGINT  , signalHandler);
        signal(SIGTERM , signalHandler);
    #ifdef Q_OS_WIN
        signal(SIGBREAK, signalHandler);
    #endif
    

    The reason you can't catch std::bad_alloc is because Qt probably uses a no-throw version of ::new. Or new is OK but the Princess is in another castle.

    There are two pitfalls you may stumble upon with your original problem (crash).

    1. It can be a reallocating issue.

    When the array is filled up already, and you try to insert more, it allocates a new array and copies (moves) data from the previous one. So you end up having two big arrays until copying (moving) is done. If you know the exact number of strings ahead, you can try preallocating the array to ensure there will be no reallocations. Use QList::reserve() for that.

    2. Qt containers like QList and QVector can hold no more than 2GB of data.

    If sizeof(QString) is 8 bytes, there will be allowed no more than 2^28 items.

    It will crash eventually if you try to store more. Try std::vector (with reserve) and check if it works.

    After all, if your system doesn't have enough memory for the task - it doesn't have enough memory, and there is nothing you can do about it but to change your algorithm.