2

私は、メモリからドキュメント ID の長いリストを読み取り、一致する ID を探すクエリ プロセッサに取り組んでいます。見つかった場合は、docid (int) とドキュメントのランク (double) を含む DOC 構造体を作成し、それを優先キューにプッシュします。私の問題は、検索された単語に長いリストがある場合に、DOC をキューにプッシュしようとすると、次の例外が発生することです。 :bad_alloc メモリ位置 0x0012ee88..

単語に短いリストがある場合、うまく機能します。コードのいくつかの場所で DOC をキューにプッシュしようとしましたが、それらはすべて特定の行まで機能します。その後、上記のエラーが発生します。読み込まれた最長のリストが 1 MB 未満であり、割り当てたすべてのメモリを解放しているため、何が問題なのか完全に途方に暮れています。DOC を保持する容量のあるキューに DOC をプッシュしようとすると、突然 bad_alloc 例外が発生するのはなぜですか?

このような質問は、すべてのコードを見ないと答えられないことはわかっていますが、ここに投稿するには長すぎます。私はできる限り多くのことを書いていますが、誰かが私に答えてくれることを切望しています。

NextGEQ 関数は、docid の圧縮ブロックのリストをブロックごとに読み取ります。つまり、ブロック内 (別のリスト内) の最後の docid が、渡された docid よりも大きいことがわかった場合、ブロックを解凍し、正しいものが見つかるまで検索します。各リストは、圧縮された各チャンクの長さとチャンク内の最後の docid を含むリストに関するメタデータで始まります。data.query は、メタデータの先頭を指します。data.metapointer は、関数が現在あるメタデータ内の任意の場所を指します。また、data.blockpointer は、圧縮されていない docid のブロックの先頭を指します (存在する場合)。すでに解凍されていることがわかった場合は、検索するだけです。以下では、関数を初めて呼び出すと、ブロックが解凍され、docid が検索されます。その後のキューへのプッシュは機能します。2回目はありません」解凍する必要さえありません。つまり、新しいメモリは割り当てられませんが、その後、キューにプッシュすると 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

4 に答える 4

1

もう1つ試すことができるのは、すべてのポインターをスマートポインターに置き換えることです。具体的にboost::shared_ptr<>は、コードの量とタスクの自動化にどれだけ慣れているかに応じて、のようなものに置き換えます。スマートポインタはすべての答えではありませんが、少なくとも生のポインタよりも安全です。

于 2010-04-08T21:01:10.450 に答える
1

QUERYMANAGER::OpenListlistnode を値で返します。次に、startlist = &OpenList(value);返された一時オブジェクトのアドレスを取得します。一時的なデータがなくなると、しばらくの間データにアクセスできる場合がありますが、その後上書きされます。スタック上でポインタ以外の listnode スタートリストを宣言し、戻り値を直接割り当てることはできますか? 次に、他の用途の前にある * を削除して、問題が解決するかどうかを確認します。

于 2010-04-08T20:11:55.570 に答える
0

ヒープの破損があり、実際にメモリを使い果たしていないと仮定すると、ヒープが破損する最も一般的な方法は、同じポインターを 2 回削除 (または解放) することです。これが問題であるかどうかは、delete (または free) のすべての呼び出しをコメントアウトするだけで簡単に見つけることができます。これにより、プログラムがふるいのようにリークしますが、実際にクラッシュしない場合は、おそらく問題を特定したことになります。

ヒープが破損するもう 1 つの一般的な原因は、ヒープに割り当てられていないポインターを削除 (または解放) することです。破損の 2 つの原因を区別することは必ずしも容易ではありませんが、破損が実際に問題であるかどうかを確認することを最優先する必要があります。

削除しようとしているものにデストラクタがあり、呼び出されないとプログラムのセマンティクスが壊れる場合、このアプローチはうまく機能しないことに注意してください。

于 2010-04-07T18:08:39.773 に答える
0

ご助力いただきありがとうございます。あなたは正しかった、ニール - 私は自分のヒープを壊すことに成功したに違いない。何が原因だったのかはまだわかりませんが、malloc(numdocids * 5) を malloc(256) に変更すると、魔法のようにクラッシュしなくなりました。malloc が実際に成功したかどうかを確認する必要があったと思います。再度、感謝します!Bsg

于 2010-04-11T21:33:45.907 に答える