c ++ stl очередь очередей вставка исключение bad_alloc - PullRequest
2 голосов
/ 07 апреля 2010

Я работаю над обработчиком запросов, который читает из памяти длинные списки идентификаторов документов и ищет совпадающие идентификаторы. Когда он находит его, он создает структуру DOC, содержащую docid (int) и ранг документа (double), и помещает его в очередь с приоритетами. Моя проблема в том, что когда искомые слова имеют длинный список, когда я пытаюсь вставить DOC в очередь, я получаю следующее исключение: Необработанное исключение в 0x7c812afb в QueryProcessor.exe: Microsoft C ++ исключение: std :: bad_alloc в ячейке памяти 0x0012ee88 ..

Когда слово имеет короткий список, оно отлично работает. Я пытался поместить DOC в очередь в нескольких местах моего кода, и все они работают до определенной строки; после этого я получаю вышеуказанную ошибку. Я совершенно не понимаю, что не так, потому что самый длинный список, который можно прочитать, составляет менее 1 МБ, и я освобождаю всю память, которую я выделяю. Почему вдруг должно возникать исключение bad_alloc, когда я пытаюсь поместить DOC в очередь, в которой есть возможность его хранить (я использовал вектор с достаточным пространством, зарезервированным в качестве базовой структуры данных для очереди с приоритетами)?

Я знаю, что на подобные вопросы почти невозможно ответить, не видя весь код, но это слишком долго, чтобы публиковать здесь. Я вкладываю столько, сколько могу, и с тревогой надеюсь, что кто-то может дать мне ответ, потому что я нахожусь в конце своего ума.

Функция NextGEQ считывает список сжатых блоков документа docids блок за блоком. То есть, если он видит, что последний документ в блоке (в отдельном списке) больше переданного документа, он распаковывает блок и выполняет поиск, пока не найдет нужный. Каждый список начинается с метаданных о списке с длинами каждого сжатого чанка и последнего документа в чанке. data.iquery указывает на начало метаданных; data.metapointer указывает на то, где в метаданных в настоящее время находится функция; и data.blockpointer указывает на начало блока несжатых документов, если он есть. Если он видит, что он уже распакован, он просто ищет. Ниже, когда я вызываю функцию в первый раз, она распаковывает блок и находит документ; толчок в очередь после этого работает. Во второй раз даже не нужно распаковывать; то есть, новая память не выделяется, но по истечении этого времени добавление в очередь приводит к ошибке bad_alloc.

Редактировать: я еще немного очистил свой код, чтобы он скомпилировался. Я также добавил в функции OpenList () и NextGEQ, хотя последний является длинным, потому что я думаю, что проблема вызвана повреждением кучи где-то в нем. Большое спасибо!

struct DOC{

    long int docid;
    long 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;
    }
    };

struct listnode{

int* metapointer;
int* blockpointer;
int docposition;
int frequency;
int numberdocs;
int* iquery;
listnode* nextnode;

};

void QUERYMANAGER::SubmitQuery(char *query){

    listnode* startlist;

        vector<DOC> docvec;
        docvec.reserve(20);
        DOC doct;


    //create a priority queue to use as a min-heap to store the documents and rankings;


        priority_queue<DOC, vector<DOC>,std::greater<DOC>> q(docvec.begin(), docvec.end());

        q.push(doct);

    //do some processing here; startlist is a pointer to a listnode struct that starts the   //linked list

        //point the linked list start pointer to the node returned by the OpenList method

        startlist = &OpenList(value);
        listnode* minpointer;
        q.push(doct);


        //start by finding the first docid in the shortest list
            int i = 0;
            q.push(doct);
            num = NextGEQ(0, *startlist);
            q.push(doct);
            while(num != -1)
               {

            q.push(doct);

    //the is where the problem starts - every previous q.push(doct) works; the one after
    //NextGEQ(num +1, *startlist) gives the bad_alloc error

            num = NextGEQ(num + 1, *startlist);

         //this is where the exception is thrown
            q.push(doct);               
        }

    }



//takes a word and returns a listnode struct with a pointer to the beginning of the list
//and metadata about the list 
listnode QUERYMANAGER::OpenList(char* word)
{
    long int numdocs;

    //create a new node in the linked list and initialize its variables


    listnode n;
    n.iquery = cache -> GetiList(word, &numdocs);
    n.docposition = 0;
    n.frequency = 0;
    n.numberdocs = numdocs;

   //an int pointer to point to where in the metadata you are
    n.metapointer = n.iquery;
    n.nextnode = NULL;
  //an int pointer to point to the uncompressed block of data, if there is one
    n.blockpointer = NULL;



    return n;


}


int QUERYMANAGER::NextGEQ(int value, listnode& data)
{
     int lengthdocids;
     int lengthfreqs; 
     int lengthpos;
     int* temp;
     int lastdocid;


     lastdocid = *(data.metapointer + 2);

while(true)
{

         //if it's not the first chunk in the list, the blockpointer will be pointing to the 
        //most recently opened block and docpos to the current position in the block
    if( data.blockpointer && lastdocid >= value)
    {

            //if the last docid in the chunk is >= the docid we're looking for,
            //go through the chunk to look for a match


        //the last docid in the block is in lastdocid; keep going until you hit it
        while(*(data.blockpointer + data.docposition) <= lastdocid)
        {
            //compare each docid with the docid passed in; if it's greater than or equal to it, return a pointer to the docid
             if(*(data.blockpointer + data.docposition ) >= value)
             {

                 //return the next greater than or equal docid
                 return *(data.blockpointer + data.docposition);
             }
             else
             {
                 ++data.docposition;
             }
        }

        //read through the whole block; couldn't find matching docid; increment metapointer to the next block;
        //free the block's memory

        data.metapointer += 3;
        lastdocid = *(data.metapointer + 3);
        free(data.blockpointer);
        data.blockpointer = NULL;
    }


        //reached the end of a block; check the metadata to find where the next block begins and ends and whether 
        //the last docid in the block is smaller or larger than the value being searched for


        //first make sure that you haven't reached the end of the list
            //if the last docid in the chunk is still smaller than the value passed in, move the metadata pointer
           //to the beginning of the next chunk's metadata; read in the new metadata


            while(true)
         //  while(*(metapointers[index]) != 0 )
           {
               if(lastdocid < value && *(data.metapointer) !=0)
               {
               data.metapointer += 3;
               lastdocid = *(data.metapointer + 2);
               }


           else if(*(data.metapointer) == 0)
           {
               return -1;
             }

           else
               //we must have hit a chunk whose lastdocid is >= value; read it in
           {
                //read in the metadata
           //the length of the chunk of docid's is cumulative, so subtract the end of the last chunk 
           //from the end of this chunk to get the length

               //find the end of the metadata


                temp = data.metapointer;

            while(*temp != 0)
            {
                temp += 3;
            }
                temp += 2;
    //temp is now pointing to the beginning of the list of compressed data; use the location of metapointer
    //to calculate where to start reading and how much to read

         //if it's the first chunk in the list,the corresponding metapointer is pointing to the beginning of the query
        //so the number of bytes of docid's is just the first integer in the metadata
                if(  data.metapointer == data.iquery)
                {
                    lengthdocids = *data.metapointer;

                }

                else
                {
                    //start reading from the offset of the end of the last chunk (saved in metapointers[index] - 3)
                    //plus 1 = the beginning of this chunk

                    lengthdocids = *(data.metapointer) - (*(data.metapointer - 3));
                    temp += (*(data.metapointer - 3)) / sizeof(int); 

                   }


           //allocate memory for an array of integers - the block of docid's uncompressed
           int* docblock = (int*)malloc(lengthdocids * 5 );

           //decompress docid's into the block of memory allocated
            s9decompress((int*)temp, lengthdocids /4, (int*) docblock, true);

            //set the blockpointer to point to the beginning of the block
            //and docpositions[index] to 0
            data.blockpointer = docblock;
            data.docposition = 0;
            break;

                }

           } 

}
}

Большое спасибо, BSG.

Ответы [ 4 ]

1 голос
/ 09 апреля 2010

QUERYMANAGER::OpenList возвращает список по значению. В startlist = &OpenList(value); вы затем берете адрес возвращаемого временного объекта. Когда временное устройство исчезнет, ​​вы сможете на некоторое время получить доступ к данным, а затем они будут перезаписаны. Не могли бы вы просто объявить стартовый список без указателя в стеке и напрямую присвоить ему возвращаемое значение? Затем удалите * перед другим использованием и посмотрите, решит ли это проблему.

1 голос
/ 09 апреля 2010

Еще одна вещь, которую вы можете попробовать, - это заменить все указатели умными указателями, в частности, что-то вроде boost::shared_ptr<>, в зависимости от того, сколько кода на самом деле это и насколько вам удобно автоматизировать задачу. Умные указатели не являются ответом на все вопросы, но они, по крайней мере, безопаснее, чем необработанные указатели.

0 голосов
/ 12 апреля 2010

Спасибо за вашу помощь. Вы были правы, Нил, мне, наверное, удалось испортить мою кучу. Я до сих пор не уверен, что было причиной, но когда я изменил malloc (numdocids * 5) на malloc (256), он волшебным образом перестал падать. Полагаю, мне следовало проверить, действительно ли мои мальлоки были успешными! Еще раз спасибо! BSG

0 голосов
/ 07 апреля 2010

Предполагая, что у вас повреждена куча, и вы не исчерпываете память, самый распространенный способ повреждения кучи - удаление (или освобождение) одного и того же указателя дважды. Вы можете легко узнать, является ли это проблемой, просто закомментировав все ваши звонки, чтобы удалить (или бесплатно). Это приведет к тому, что ваша программа протечет как сито, но если это не произойдет сбой, вы, вероятно, выявили проблему.

Другой распространенной причиной поврежденной кучи является удаление (или освобождение) указателя, который никогда не выделялся в куче. Разграничить две причины коррупции не всегда легко, но вашей первоочередной задачей должно быть выяснение, является ли коррупция проблемой.

Обратите внимание, что этот подход не будет работать слишком хорошо, если удаляемые объекты имеют деструкторы, которые, если их не вызывать, нарушают семантику вашей программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...