views:

263

answers:

4

Hi,

I am writing a query processor which allocates large amounts of memory and tries to find matching documents. Whenever I find a match, I create a structure to hold two variables describing the document and add it to a priority queue. Since there is no way of knowing how many times I will do this, I tried creating my structs dynamically using new. When I pop a struct off the priority queue, the queue (STL priority queue implementation) is supposed to call the object's destructor. My struct code has no destructor, so I assume a default destructor is called in that case.

However, the very first time that I try to create a DOC struct, I get the following error:

Unhandled exception at 0x7c812afb in QueryProcessor.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0012f5dc..

I don't understand what's happening - have I used up so much memory that the heap is full? It doesn't seem likely. And it's not as if I've even used that pointer before.

So: first of all, what am I doing that's causing the error, and secondly, will the following code work more than once? Do I need to have a separate pointer for each struct created, or can I re-use the same temporary pointer and assume that the queue will keep a pointer to each struct?

Here is my code:

struct DOC{
    int docid;
    double rank;

    public:
        DOC()
        {
            docid = 0;
            rank = 0.0;
        }

        DOC(int num, double ranking)
        {
            docid = num;
            rank = ranking;

        }

        bool operator>( const DOC & d ) const {
           return rank > d.rank;
        }

        bool operator<( const DOC & d ) const {
           return rank < d.rank;
        }
};


//a lot of processing goes on here

        priority_queue<DOC, std::vector<DOC>, std::greater<DOC>> q;

//when a matching document is found, I do this:

rank = calculateRanking(table, num);

    //if the heap is not full, create a DOC struct with the docid and rank and add it to the heap
    if(q.size() < 20)
    {
        doc = new DOC(num, rank);
        q.push(*doc);
        doc = NULL;
    }

    //if the heap is full, but the new rank is greater than the 
    //smallest element in the min heap, remove the current smallest element
    //and add the new one to the heap
    else if(rank > q.top().rank)
    {
        q.pop();

        cout << "pushing doc on to queue" << endl;
        doc = new DOC(num, rank);
        q.push(*doc);
    }

Thank you very much, bsg.

A: 

std::bad_alloc is thrown when you are out of memory.

You need to free the pointer you are about to pop when you do the q.pop(), else you are leaking. If you have a LOT of elements this could be your problem.

Doc *p = q.front();
delete p;
q.pop();

As others have mentioned, if you declare your queue to hold Doc's instead of Doc*'s, then you don't have to manage memory yourself and the container will do it for you.

I know. But how would I go about figuring that out?
bsg
My queue does hold DOC's, not DOC*'s. And thanks for the tip about the popping - does it still apply if my queue holds DOC's and not DOC*'s?
bsg
Went into lower-level code and discovered that the error occurs here: // allocate storage for _Count elements of type _Ty return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));00421B08 mov eax,dword ptr [_Count] 00421B0B shl eax,4 00421B0E push eax 00421B0F call operator new (4117DFh) 00421B14 add esp,4 typename A::template rebind<T>::other::pointer #define _REFERENCE_X(T, A) \ typename A::template rebind<T>::other::referenceIt looks like it's having problems allocating memory for the queue element. Any idea why that would be?
bsg
A: 

About the exception: The class bad_alloc has a member function what() which returns a const char * to a string containing a readable description of the cause of the error.

EDIT: If you are considering storing a dynamically allocated object, store the pointer and not a reference to the object.

themoondothshine
A: 

You have a memory leak. The insertion methods for STL containers store a copy of the type you pass in so you don't need to allocate doc on the heap.

Instead of

doc = new DOC(...)
q.push(*doc);
doc = NULL;

Do either

doc(...);
q.push(doc);

Or

doc = new DOC(...);
q.push(*doc);
delete doc;
doc = NULL;
MSN
I followed your advice and created one DOC at the beginning of the method (statically allocated) and then did the following:if(q.size() < 20) { doc.docid = num; doc.rank = rank; q.push(doc); }The variable assign fine, but when I try to push the doc on I get a bad_alloc error again. What's wrong?
bsg
@bsg, without more info like a callstack or knowing what else you are doing it will be very difficult to debug this.
MSN
+2  A: 

Why are you creating the structure in the following on the heap:

doc = new DOC(num, rank);
q.push(*doc);

This first creates a DOC on the heap, then stores a copy of the object in the queue and subsequently leaks the dynamically created DOC.

The following would be sufficient and doesn't leak:

q.push(DOC(num, rank));
Georg Fritzsche