سؤال

أنا أعمل على معالج استعلام يقرأ في قوائم طويلة من معرفات المستند من الذاكرة وأبحث عن مطابقة المعرف. عندما يجد واحدة ، فإنه ينشئ هيكل مستند يحتوي على المستند (INT) ورتبة المستند (مزدوج) ويدفعه إلى قائمة انتظار ذات أولوية. مشكلتي هي أنه عندما يكون للكلمة (s) التي تم البحث عنها قائمة طويلة ، عندما أحاول دفع المستند إلى قائمة الانتظار ، أحصل على الاستثناء التالي: استثناء غير معقول في 0x7C812AFB في QueryProcessor.exe: Microsoft C ++ استثناء: الأمراض المنقولة جنسيا: std: : BAD_ALLOC في موقع الذاكرة 0x0012EE88 ..

عندما يكون للكلمة قائمة قصيرة ، فإنها تعمل بشكل جيد. حاولت دفع Doc إلى قائمة الانتظار في عدة أماكن في الكود الخاص بي ، وكلهم يعملون حتى خط معين ؛ بعد ذلك ، أحصل على الخطأ أعلاه. أنا في حيرة تمامًا فيما يتعلق بما هو الخطأ لأن أطول قائمة تقرأ في أقل من 1 ميغابايت وأتحرر كل الذاكرة التي أخصصها. لماذا يجب أن يكون هناك فجأة استثناء bad_alloc عندما أحاول دفع مستند إلى قائمة انتظار لها القدرة على الاحتفاظ بها (استخدمت متجهًا مع مساحة كافية محفوظة كهيكل بيانات أساسي لقائمة انتظار الأولوية)؟

أعلم أن أسئلة كهذه تقريبًا مستحيل الإجابة دون رؤية كل الكود ، لكن الوقت طويل جدًا لنشره هنا. أنا أضع بقدر ما أستطيع ، وآمل بقلق أن يتمكن شخص ما من إعطائي إجابة ، لأنني في نهاية ذكاء.

تقرأ وظيفة NextGeQ قائمة من الكتل المضغوطة من كتلة المستندات حسب الكتلة. هذا هو ، إذا رأى أن LastDocid في الكتلة (في قائمة منفصلة) أكبر من المستند الذي تم تمريره ، فإنه يقوم بإلغاء ضغط الكتلة ويفحص حتى يجد الحالة الصحيح. تبدأ كل قائمة بالبيانات الوصفية حول القائمة بأطوال كل قطعة مضغوطة وآخر مستند في الجزء. 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.

هل كانت مفيدة؟

المحلول

على افتراض أن لديك فسادًا في الكومة وليست في الواقع ذاكرة مرهقة ، فإن الطريقة الأكثر شيوعًا التي يمكن أن يفسدها الكومة هي حذف (أو تحرير) نفس المؤشر مرتين. يمكنك بسهولة معرفة ما إذا كانت هذه هي المشكلة بمجرد التعليق على جميع مكالماتك لحذف (أو مجانًا). سيؤدي ذلك إلى تسرب برنامجك مثل الغربال ، ولكن إذا لم يعطل بالفعل ، فمن المحتمل أن تكون قد حددت المشكلة.

السبب الشائع الآخر للسيدة الفاسدة هو حذف (أو تحرير) مؤشر لم يتم تخصيصه على الإطلاق على الكومة. ليس من السهل دائمًا التمييز بين سببي الفساد ، ولكن يجب أن يكون أولويتك الأولى لمعرفة ما إذا كان الفساد هو المشكلة بالفعل.

لاحظ أن هذا النهج لن يعمل بشكل جيد للغاية إذا كانت الأشياء التي تحذفها لها مدمرة ، إذا لم تتم تسميتها ، فهي لا تسمى دلالات برنامجك.

نصائح أخرى

QUERYMANAGER::OpenList إرجاع listNode حسب القيمة. في startlist = &OpenList(value); ثم تشرع في أخذ عنوان الكائن المؤقت الذي تم إرجاعه. عندما تختفي المؤقتة ، قد تتمكن من الوصول إلى البيانات لفترة من الوقت ثم يتم الكتابة عليها. هل يمكنك فقط إعلان قائمة بدء ListNode غير المؤسسة على المكدس وتعيين قيمة الإرجاع مباشرة؟ ثم قم بإزالة * أمام الاستخدامات الأخرى ومعرفة ما إذا كان ذلك يحدد المشكلة.

شيء آخر يمكنك تجربته هو استبدال جميع المؤشرات بمؤشرات ذكية ، وتحديداً شيء مثل boost::shared_ptr<>, ، بناءً على مقدار الرمز الذي يكون هذا بالفعل ومدى راحةك في أتمتة المهمة. المؤشرات الذكية ليست هي الحل على كل شيء ، لكنها على الأقل أكثر أمانًا من المؤشرات الخام.

شكرا لجميع التعليمات الخاصة بك. كنت على حق ، نيل - يجب أن أتمكن من إفساد كومة بلدي. ما زلت غير متأكد من ما كان يسبب ذلك ، لكن عندما غيرت Malloc (numDocids * 5) إلى Malloc (256) توقفت بطريقة سحرية. أفترض أنه كان عليّ أن أتحقق مما إذا كانت Mallocs الخاصة بي تنجح بالفعل! شكرًا لك مرة أخرى! BSG

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top