سؤال

أود أن تمنع أو التعامل مع StackOverflowException هذا أنا من دعوة إلى XslCompiledTransform.Transform طريقة داخل Xsl Editor أنا أكتب.يبدو أن المشكلة التي يمكن للمستخدم كتابة Xsl script التي هي بلا حدود العودية, و ينفجر على الدعوة إلى Transform الأسلوب.(أي أن المشكلة ليست فقط نموذجية برنامجي خطأ ، والتي عادة ما سبب هذا الاستثناء.)

هل هناك طريقة للكشف عن و/أو الحد من كم recursions هل مسموح ؟ أو أي أفكار أخرى للحفاظ على هذه المدونة من مجرد تهب على لي ؟

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

المحلول

من مايكروسوفت:

بدءا .NET Framework الإصدار 2.0 ، StackOverflowException كائن لا يمكن أن تكون اشتعلت من قبل المحاولة-catch كتلة عملية المقابلة هو إنهاء بشكل افتراضي.وبالتالي ، وينصح المستخدمين إلى كتابة التعليمات البرمجية الخاصة بهم لكشف ومنع كومة تجاوز.على سبيل المثال, إذا كان لديك التطبيق يعتمد على العودية ، استخدام العداد أو حالة إلى حالة إنهاء حلقة متكررة.

أفترض الاستثناء يحدث في داخلي .صافي الأسلوب وليس في التعليمات البرمجية الخاصة بك.

يمكنك أن تفعل بعض الأشياء.

  • كتابة التعليمات البرمجية التي يتحقق xsl لانهائية العودية و بإعلام المستخدم قبل تطبيق تحويل (لاف).
  • تحميل XslTransform رمز في عملية منفصلة (Hacky ، ولكن أقل من العمل).

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

تحرير:أنا مجرد اختبار, هنا هو كيف نفعل ذلك:

MainProcess:

// This is just an example, obviously you'll want to pass args to this.
Process p1 = new Process();
p1.StartInfo.FileName = "ApplyTransform.exe";
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

p1.Start();
p1.WaitForExit();

if (p1.ExitCode == 1)    
   Console.WriteLine("StackOverflow was thrown");

ApplyTransform العملية:

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        throw new StackOverflowException();
    }

    // We trap this, we can't save the process, 
    // but we can prevent the "ILLEGAL OPERATION" window 
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        if (e.IsTerminating)
        {
            Environment.Exit(1);
        }
    }
}

نصائح أخرى

ملاحظة السؤال في فضله بواسطة @WilliamJockusch الأصلي مسألة مختلفة.

هذا الجواب عن ستاكوفيرفلوو في الحالة العامة من طرف ثالث المكتبات و ما يمكن/لا يمكن القيام به معهم.إذا كنت تبحث عن حالة خاصة مع XslTransform ، انظر الجواب المقبول.


كومة تجاوزات تحدث بسبب البيانات على كومة يتجاوز حدا معينا (بالبايت).تفاصيل كيف يعمل كشف يمكن العثور عليها هنا.

أنا أتساءل عما اذا كان هناك طريقة لتعقب StackOverflowExceptions.وبعبارة أخرى, أعتقد أني لا حصر له الإعادة في مكان ما في قانون بلدي, ولكن ليس لدي أي فكرة عن المكان.أريد أن تعقب عليه من قبل بعض يعني أنه أسهل من التنقل خلال التعليمات البرمجية في كل مكان حتى أرى ذلك يحدث.لا يهمني كيف hackish هو.

كما ذكرت في الرابط الكشف عن تجاوز سعة مكدس من تحليل رمز ثابت يتطلب حل مشكلة لوقف وهو غير مقرر.والآن بعد أن عرفنا أن لا يوجد حل سحري, أنا يمكن أن تظهر لك بعض الحيل التي أعتقد يساعد على تعقب المشكلة.

أعتقد أن هذا السؤال يمكن أن تفسر بطرق مختلفة ، وبما أنني قليلا بالملل : -) سوف كسرها نزولا إلى أشكال مختلفة.

الكشف عن تجاوز سعة مكدس في بيئة اختبار

أساسا المشكلة هنا هو أن يكون لديك (محدودة) اختبار البيئة و ترغب في الكشف عن تجاوز سعة مكدس في (موسع) بيئة الإنتاج.

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

أساسا أنا محاولة ذلك من خلال جعل كومة عمق صغيرة قدر الإمكان.إذا كان لا تجاوز, أنا يمكن أن تجعل دائما أكبر (=في هذه الحالة:أكثر أمانا) على بيئة الإنتاج.لحظة تحصل على تجاوز سعة المكدس, يمكنك يدويا تقرر إذا كان 'صالح' واحد أو لا.

للقيام بذلك, تمرير حجم مكدس (في حالتنا:قيمة صغيرة) إلى موضوع المعلمة ، ونرى ما سيحدث.الافتراضي حجم مكدس في .نت 1 ميجا بايت, ونحن في طريقنا إلى استخدام وسيلة قيمة أصغر:

class StackOverflowDetector
{
    static int Recur()
    {
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        int depth = 1 + Recur();
    }

    static void Main(string[] args)
    {
        Thread t = new Thread(Start, 1);
        t.Start();
        t.Join();
        Console.WriteLine();
        Console.ReadLine();
    }
}

ملاحظة:نحن ذاهبون إلى استخدام هذه التعليمة البرمجية أدناه أيضا.

بمجرد أن يفيض ، يمكنك تعيين إلى أكبر قيمة حتى تحصل على ذلك أن من المنطقي.

إنشاء استثناءات قبل ذلك

على StackOverflowException لا catchable.وهذا يعني ليس هناك الكثير يمكنك القيام به عندما حدث ذلك.لذا ، إذا كنت تعتقد شيئا لا بد أن تذهب الخطأ في التعليمات البرمجية الخاصة بك ، يمكنك أن تجعل الخاصة بك استثناء في بعض الحالات.الشيء الوحيد الذي كنت بحاجة للحصول على هذا المكدس الحالي العمق ؛ هناك حاجة العداد ، يمكنك استخدام القيم الحقيقية من .صافي:

class StackOverflowDetector
{
    static void CheckStackDepth()
    {
        if (new StackTrace().FrameCount > 10) // some arbitrary limit
        {
            throw new StackOverflowException("Bad thread.");
        }
    }

    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
        Console.WriteLine();
        Console.ReadLine();
    }
}

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

كشف في موضوع مستقل

صراحة اقترح هذا, حتى هنا يذهب هذا واحد.

يمكنك محاولة الكشف عن ذلك في موضوع مستقل..ولكن ربما لن تفعل لك أي خير.تجاوز سعة مكدس يمكن أن يحدث سريع, حتى قبل أن تحصل على تبديل السياق.وهذا يعني أن هذه الآلية ليست موثوقة في جميع... أنا لا أوصي في الواقع استخدامه.كان متعة لبناء ورغم ذلك ها هو الكود :-)

class StackOverflowDetector
{
    static int Recur()
    {
        Thread.Sleep(1); // simulate that we're actually doing something :-)
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
    }

    static void Main(string[] args)
    {
        // Prepare the execution thread
        Thread t = new Thread(Start);
        t.Priority = ThreadPriority.Lowest;

        // Create the watch thread
        Thread watcher = new Thread(Watcher);
        watcher.Priority = ThreadPriority.Highest;
        watcher.Start(t);

        // Start the execution thread
        t.Start();
        t.Join();

        watcher.Abort();
        Console.WriteLine();
        Console.ReadLine();
    }

    private static void Watcher(object o)
    {
        Thread towatch = (Thread)o;

        while (true)
        {
            if (towatch.ThreadState == System.Threading.ThreadState.Running)
            {
                towatch.Suspend();
                var frames = new System.Diagnostics.StackTrace(towatch, false);
                if (frames.FrameCount > 20)
                {
                    towatch.Resume();
                    towatch.Abort("Bad bad thread!");
                }
                else
                {
                    towatch.Resume();
                }
            }
        }
    }
}

تشغيل هذا في المصحح والمتعة ما يحدث.

باستخدام خصائص تجاوز سعة مكدس

آخر تفسير سؤالك هو:"أين هي قطعة من التعليمات البرمجية التي يمكن أن تسبب استثناء تجاوز سعة مكدس?".من الواضح أن الجواب على ذلك هو:كل رمز مع العودية.لكل قطعة من التعليمات البرمجية ، يمكنك بعد ذلك القيام ببعض التحليل اليدوي.

ومن الممكن أيضا تحديد ذلك باستخدام تحليل رمز ثابت.ما تحتاج إلى القيام به هو فك جميع طرق لمعرفة إذا كانت تحتوي على لانهائي العودية.هنا بعض التعليمات البرمجية التي تفعل ذلك بالنسبة لك:

// A simple decompiler that extracts all method tokens (that is: call, callvirt, newobj in IL)
internal class Decompiler
{
    private Decompiler() { }

    static Decompiler()
    {
        singleByteOpcodes = new OpCode[0x100];
        multiByteOpcodes = new OpCode[0x100];
        FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
        for (int num1 = 0; num1 < infoArray1.Length; num1++)
        {
            FieldInfo info1 = infoArray1[num1];
            if (info1.FieldType == typeof(OpCode))
            {
                OpCode code1 = (OpCode)info1.GetValue(null);
                ushort num2 = (ushort)code1.Value;
                if (num2 < 0x100)
                {
                    singleByteOpcodes[(int)num2] = code1;
                }
                else
                {
                    if ((num2 & 0xff00) != 0xfe00)
                    {
                        throw new Exception("Invalid opcode: " + num2.ToString());
                    }
                    multiByteOpcodes[num2 & 0xff] = code1;
                }
            }
        }
    }

    private static OpCode[] singleByteOpcodes;
    private static OpCode[] multiByteOpcodes;

    public static MethodBase[] Decompile(MethodBase mi, byte[] ildata)
    {
        HashSet<MethodBase> result = new HashSet<MethodBase>();

        Module module = mi.Module;

        int position = 0;
        while (position < ildata.Length)
        {
            OpCode code = OpCodes.Nop;

            ushort b = ildata[position++];
            if (b != 0xfe)
            {
                code = singleByteOpcodes[b];
            }
            else
            {
                b = ildata[position++];
                code = multiByteOpcodes[b];
                b |= (ushort)(0xfe00);
            }

            switch (code.OperandType)
            {
                case OperandType.InlineNone:
                    break;
                case OperandType.ShortInlineBrTarget:
                case OperandType.ShortInlineI:
                case OperandType.ShortInlineVar:
                    position += 1;
                    break;
                case OperandType.InlineVar:
                    position += 2;
                    break;
                case OperandType.InlineBrTarget:
                case OperandType.InlineField:
                case OperandType.InlineI:
                case OperandType.InlineSig:
                case OperandType.InlineString:
                case OperandType.InlineTok:
                case OperandType.InlineType:
                case OperandType.ShortInlineR:
                    position += 4;
                    break;
                case OperandType.InlineR:
                case OperandType.InlineI8:
                    position += 8;
                    break;
                case OperandType.InlineSwitch:
                    int count = BitConverter.ToInt32(ildata, position);
                    position += count * 4 + 4;
                    break;

                case OperandType.InlineMethod:
                    int methodId = BitConverter.ToInt32(ildata, position);
                    position += 4;
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes));
                        }
                        else
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()));
                        }
                    }
                    catch { } 
                    break;


                default:
                    throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
            }
        }
        return result.ToArray();
    }
}

class StackOverflowDetector
{
    // This method will be found:
    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        RecursionDetector();
        Console.WriteLine();
        Console.ReadLine();
    }

    static void RecursionDetector()
    {
        // First decompile all methods in the assembly:
        Dictionary<MethodBase, MethodBase[]> calling = new Dictionary<MethodBase, MethodBase[]>();
        var assembly = typeof(StackOverflowDetector).Assembly;

        foreach (var type in assembly.GetTypes())
        {
            foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).OfType<MethodBase>())
            {
                var body = member.GetMethodBody();
                if (body!=null)
                {
                    var bytes = body.GetILAsByteArray();
                    if (bytes != null)
                    {
                        // Store all the calls of this method:
                        var calls = Decompiler.Decompile(member, bytes);
                        calling[member] = calls;
                    }
                }
            }
        }

        // Check every method:
        foreach (var method in calling.Keys)
        {
            // If method A -> ... -> method A, we have a possible infinite recursion
            CheckRecursion(method, calling, new HashSet<MethodBase>());
        }
    }

الآن حقيقة أن أسلوب دورة يحتوي على العودية ، وهذا لا يعني ضمان أن تجاوز سعة مكدس سوف يحدث - انها مجرد الأرجح شرطا الخاص بك استثناء تجاوز سعة مكدس.باختصار ، هذا يعني أن هذا القانون سوف تحدد قطعة من التعليمات البرمجية حيث تجاوز سعة مكدس يمكن تحدث التي يجب تضييق معظم رمز إلى حد كبير.

بعد النهج الأخرى

هناك بعض الأساليب الأخرى التي يمكنك محاولة لم الموصوفة هنا.

  1. التعامل مع تجاوز سعة مكدس من خلال استضافة CLR عملية التعامل مع الأمر.علما أن كنت لا تزال لا يمكن 'الصيد' عليه.
  2. تغيير جميع IL رمز مبنى آخر DLL, إضافة الشيكات على العودية.نعم هذا ممكن جدا (أنا قد نفذت في الماضي :-);انها مجرد من الصعب ينطوي على الكثير من التعليمات البرمجية للحصول على ذلك الحق.
  3. استخدام .صافي التنميط API للقبض على جميع المكالمات الأسلوب واستخدام ذلك إلى معرفة كومة تجاوزات.على سبيل المثال, يمكنك تنفيذ الشيكات أنه إذا كنت تواجه نفس الأسلوب X مرات في شجرة تعطي إشارة.هناك مشروع هنا من شأنها أن تعطي لك السبق.

وأود أن أقترح خلق التفاف حول كائن XmlWriter، لذلك سيكون الاعتماد كمية المكالمات إلى WriteStartElement / WriteEndElement، وإذا كنت تحد كمية من الأكواد إلى رقم (الحديد 100)، هل سيكون قادرا على رمي استثناء مختلفة، على سبيل المثال - InvalidOperation

وينبغي أن حل المشكلة في معظم الحالات

public class LimitedDepthXmlWriter : XmlWriter
{
    private readonly XmlWriter _innerWriter;
    private readonly int _maxDepth;
    private int _depth;

    public LimitedDepthXmlWriter(XmlWriter innerWriter): this(innerWriter, 100)
    {
    }

    public LimitedDepthXmlWriter(XmlWriter innerWriter, int maxDepth)
    {
        _maxDepth = maxDepth;
        _innerWriter = innerWriter;
    }

    public override void Close()
    {
        _innerWriter.Close();
    }

    public override void Flush()
    {
        _innerWriter.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return _innerWriter.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        _innerWriter.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        _innerWriter.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        _innerWriter.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        _innerWriter.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        _innerWriter.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        _innerWriter.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        _innerWriter.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        _innerWriter.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        _depth--;

        _innerWriter.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        _innerWriter.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        _innerWriter.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        _innerWriter.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        _innerWriter.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        _innerWriter.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        _innerWriter.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument(bool standalone)
    {
        _innerWriter.WriteStartDocument(standalone);
    }

    public override void WriteStartDocument()
    {
        _innerWriter.WriteStartDocument();
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        if (_depth++ > _maxDepth) ThrowException();

        _innerWriter.WriteStartElement(prefix, localName, ns);
    }

    public override WriteState WriteState
    {
        get { return _innerWriter.WriteState; }
    }

    public override void WriteString(string text)
    {
        _innerWriter.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        _innerWriter.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteWhitespace(string ws)
    {
        _innerWriter.WriteWhitespace(ws);
    }

    private void ThrowException()
    {
        throw new InvalidOperationException(string.Format("Result xml has more than {0} nested tags. It is possible that xslt transformation contains an endless recursive call.", _maxDepth));
    }
}

هذا الجواب هو @WilliamJockusch.

أنا أتساءل عما اذا كان هناك طريقة لتعقب StackOverflowExceptions.وبعبارة أخرى, أعتقد أني لانهائية العودية في مكان ما في قانون بلدي, ولكن ليس لدي أي فكرة عن المكان.أريد أن تعقب عليه من قبل بعض يعني أنه أسهل من التنقل خلال التعليمات البرمجية في كل مكان حتى أرى ذلك يحدث.لا يهمني كيف hackish هو.على سبيل المثال, سيكون أمرا رائعا أن يكون لها وحدة يمكنني تفعيل ، وربما حتى من موضوع آخر, أن استطلاع المكدس عمق واشتكى إذا وصلت إلى مستوى لا تعتبر "عالية جدا." بالنسبة على سبيل المثال, ربما مجموعة "عالية جدا" إلى 600 إطارات ، كشف أنه إذا كان كومة كانت عميقة جدا ، يجب أن يكون مشكلة.شيء من هذا القبيل ممكن.مثال آخر سيكون تسجيل كل 1000 استدعاء الأسلوب داخل بلدي رمز إلى إخراج التصحيح.فرص هذا سوف تحصل على بعض الأدلة من overlow سيكون من الجيد جدا ، ومن المرجح انه لن تفجير الإخراج سيئا للغاية.والمفتاح هو أنه لا يمكن أن تنطوي على كتابة شيك أينما تجاوز يحدث.لأن كامل المشكلة هي أنني لا أعرف أين هو.ويفضل الحل يجب أن لا تعتمد على ما وتنمية البيئة تبدو مثل ؛ أنا.ه ، فإنه لا ينبغي أن assumet أن أنا باستخدام C# عبر أدوات محددة (مثلا ، مقابل).

يبدو أنك حريصة على سماع بعض تقنيات التصحيح للقبض على هذا ستاكوفيرفلوو حتى ظننت أنني سوف تتقاسم عدة لمحاولة لكم.

1.تفريغ الذاكرة.

برو:تفريغ الذاكرة هي متأكد النار طريقة العمل بها سببا في تجاوز سعة مكدس.C# MVP & عملت معا استكشاف الأخطاء وإصلاحها حتى ذهب إلى بلوق عن ذلك هنا.

هذه الطريقة هي أسرع وسيلة لتعقب المشكلة.

هذه الطريقة لن تحتاج إلى إعادة إنتاج المشكلات من خلال الخطوات التالية ينظر في السجلات.

يخدع:تفريغ الذاكرة كبيرة جدا و عليك أن نعلق AdPlus/procdump العملية.

2.الجانب الموجه البرمجة.

برو:وربما هذا هو أسهل طريقة لتنفيذ التعليمات البرمجية بالتحقق حجم مكدس الاستدعاءات من أي وسيلة دون كتابة التعليمات البرمجية في كل طريقة من التطبيق الخاص بك.هناك مجموعة من اوب الأطر التي تسمح لك أن اعتراض قبل وبعد المكالمات.

سوف اقول لكم الطرق التي تسبب تجاوز سعة مكدس.

يسمح لك للتحقق من StackTrace().FrameCount في الدخول والخروج من جميع الطرق في التطبيق الخاص بك.

يخدع:وسوف يكون لها تأثير الأداء - السنانير هي جزء لا يتجزأ في IL كل طريقة لا يمكنك حقا "إلغاء تنشيط" بها.

ذلك يعتمد إلى حد ما على بيئة التطوير الخاصة بك مجموعة أداة.

3.تسجيل نشاط المستخدم.

قبل أسبوع كنت في محاولة لمطاردة عدة من الصعب إعادة إنتاج المشاكل.لقد نشرت هذا QA نشاط المستخدم تسجيل القياس (و المتغيرات العالمية استثناء معالجات) .الاستنتاج لقد جاء كان حقا مستخدم بسيطة-الإجراءات-مسجل لمعرفة كيفية استخراج مشاكل في مصحح الأخطاء عند أي استثناء غير معالج يحدث.

برو:يمكنك تشغيل أو إيقاف تشغيله في الإرادة (أي الاشتراك في الأحداث).

تتبع إجراءات المستخدم لا تتطلب اعتراض كل طريقة.

يمكنك الاعتماد على عدد من الأحداث أساليب تشترك جدا الآن ببساطة أكثر من اوب.

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

يمكن أن تساعدك على فهم كيفية استخدام التطبيق الخاص بك.

يخدع:ليست مناسبة خدمة ويندوز و أنا متأكد من أن هناك أفضل الأدوات مثل هذا لتطبيقات الويب.

لا بالضرورة اقول لكم الطرق التي تتسبب في تجاوز سعة مكدس.

تتطلب خطوة من خلال السجلات يدويا استنساخ المشاكل بدلا من تفريغ الذاكرة حيث يمكنك الحصول عليها و تصحيح ذلك على الفور.

 


وربما كنت قد تحاول كل تقنيات ذكرت أعلاه و بعض @atlaste نشر و قل لنا أي واحد كنت أسهل/أسرع/أقذر/الأكثر قبولا لتشغيل في همز البيئة/الخ.

على كل حال بالتوفيق تعقب هذا.

إذا تطبيق يعتمد على رمز الحزب 3D (في XSL بين النصوص) ثم عليك أن تقرر أولا هل تريد أن تدافع عن الخلل فيها أم لا. إذا كنت تريد حقا للدفاع ثم أعتقد أن عليك أن تنفيذ المنطق الخاص الذي عرضة للأخطاء الخارجية في AppDomains منفصلة. اصطياد StackOverflowException ليست جيدة.

وتحقق أيضا هذا السؤال .

وكان لي ستاكوفيرفلوو اليوم وأنا أقرأ بعض مواضيعك وقررت أن أساعد في Collecter القمامة.

ولقد كانت لدينا حلقة لانهائية القريب مثل هذا:

    class Foo
    {
        public Foo()
        {
            Go();
        }

        public void Go()
        {
            for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
            {
                byte[] b = new byte[1]; // Causes stackoverflow
            }
        }
    }

وبدلا من ذلك السماح للتشغيل الموارد خارج النطاق مثل هذا:

class Foo
{
    public Foo()
    {
        GoHelper();
    }

    public void GoHelper()
    {
        for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
        {
            Go();
        }
    }

    public void Go()
    {
        byte[] b = new byte[1]; // Will get cleaned by GC
    }   // right now
}

وعملت بالنسبة لي، آمل أن يساعد شخص ما.

ومع NET 4.0 يمكنك إضافة سمة HandleProcessCorruptedStateExceptions من System.Runtime.ExceptionServices إلى الطريقة التي تحتوي على كتلة حاول / catch. هذا العمل حقا! ربما لا ينصح ولكنها تعمل.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.ExceptionServices;

namespace ExceptionCatching
{
    public class Test
    {
        public void StackOverflow()
        {
            StackOverflow();
        }

        public void CustomException()
        {
            throw new Exception();
        }

        public unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }
    }

    class Program
    {
        [HandleProcessCorruptedStateExceptions]
        static void Main(string[] args)
        {
            Test test = new Test();
            try {
                //test.StackOverflow();
                test.AccessViolation();
                //test.CustomException();
            }
            catch
            {
                Console.WriteLine("Caught.");
            }

            Console.WriteLine("End of program");

        }

    }      
}

@WilliamJockusch ، إذا فهمت بشكل صحيح الخاص بك القلق, ليس من الممكن (من وجهة نظر رياضية) إلى دائما تحديد لانهائي العودية كما يعني حل وقف المشكلة.لحل ذلك كنت في حاجة الى سوبر-خوارزمية العودية (مثل التجربة والخطأ المسندات على سبيل المثال) أو الآلة التي يمكن أن hypercompute (مثال على ذلك هو موضح في القسم التالي - كما تتوفر معاينة - من هذا الكتاب).

من وجهة النظر العملية ، عليك أن تعرف:

  • كم كومة الذاكرة كنت قد تركت في وقت معين
  • كم كومة الذاكرة الخاصة بك العودية الطريقة سوف تحتاج في وقت معين لمدة محددة الانتاج.

نضع في اعتبارنا أن مع الأجهزة الحالية ، هذه البيانات للغاية قابلة للتغيير بسبب تعدد المهام و لم أسمع من البرامج التي لا مهمة.

اسمحوا لي أن أعرف إذا كان هناك شيء غير واضح.

يبدو من ذلك ، وبصرف النظر عن انطلاق عملية أخرى, هناك لا يبدو أن هناك أي طريقة التعامل مع StackOverflowException.قبل أي شخص آخر يسأل حاولت استخدام AppDomain, لكن ذلك لم ينجح:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

namespace StackOverflowExceptionAppDomainTest
{
    class Program
    {
        static void recrusiveAlgorithm()
        {
            recrusiveAlgorithm();
        }
        static void Main(string[] args)
        {
            if(args.Length>0&&args[0]=="--child")
            {
                recrusiveAlgorithm();
            }
            else
            {
                var domain = AppDomain.CreateDomain("Child domain to test StackOverflowException in.");
                domain.ExecuteAssembly(Assembly.GetEntryAssembly().CodeBase, new[] { "--child" });
                domain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) =>
                {
                    Console.WriteLine("Detected unhandled exception: " + e.ExceptionObject.ToString());
                };
                while (true)
                {
                    Console.WriteLine("*");
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

إذا كنت تفعل في نهاية المطاف باستخدام منفصلة-عملية الحل ، ومع ذلك ، أود أن أوصي باستخدام Process.Exited و Process.StandardOutput والتعامل مع الأخطاء نفسك إلى إعطاء المستخدمين تجربة أفضل.

ويمكنك تقرأ هذه الخاصية كل بضع المكالمات، Environment.StackTrace، وإذا اكسسيديد على تتبع مكدس الذاكرة المؤقتة عتبة معينة التي مسبقا، يمكنك العودة وظيفة.

ويجب عليك أيضا محاولة استبدال بعض الوظائف متكررة مع الحلقات.

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