Stack Overflow ایک کال اسٹیک اوورفلو خرابی (java.lang.StackOverflowError) ہے جو تھریڈ کی زیادہ سے زیادہ اسٹیک گہرائی سے تجاوز کرنے پر ہوتی ہے۔ Java Virtual Machine Specification کے مطابق، 64 بٹ سسٹمز کے لیے JVM میں عام اسٹیک گہرائی 1024 فریم ہے۔ بنیادی وجہ بنیادی شرط کے بغیر لامتناہی ریکرشن ہے۔
اہم نکات
StackOverflowError جاوا ورچوئل مشین (JVM) یا Android Runtime (ART) کی ایک مہلک خرابی ہے جو اس وقت ہوتی ہے جب کسی تھریڈ کا کال اسٹیک زیادہ سے زیادہ قابل اجازت گہرائی تک پہنچ جاتا ہے۔ OutOfMemoryError (Heap کی کمی) کے برعکس، StackOverflowError میموری کے ایک مختلف علاقے — اسٹیک سے متعلق ہے، جہاں میتھڈ کال فریم اور مقامی متغیرات محفوظ ہوتے ہیں۔
ہر میتھڈ کال اسٹیک میں ایک فریم بناتی ہے: واپسی کا پتہ، پیرامیٹرز اور مقامی متغیرات۔ میتھڈ سے واپسی پر فریم ختم ہو جاتا ہے۔ اگر کوئی میتھڈ بنیادی شرط کے بغیر خود کو کال کرتا ہے (ریکرشن)، تو فریم اسٹیک بھرنے تک جمع ہوتے رہتے ہیں۔ JVM نیا فریم مختص نہیں کر سکتا اور «null» پیغام (Java میں) یا لامتناہی طور پر دہرائی جانے والی اسٹیک لائن کے اشارے کے ساتھ StackOverflowError پھینکتا ہے۔
تھریڈ کے اسٹیک کا سائز تخلیق کے وقت مقرر ہوتا ہے اور عملدرآمد کے دوران تبدیل نہیں ہوتا۔ Android میں، مرکزی تھریڈ کا عام اسٹیک سائز 32–48 KB ہے، جو بہت زیادہ مقامی متغیرات کے بغیر میتھڈز کے لیے تقریباً 512–1024 فریم کی گہرائی دیتا ہے۔ پس منظر کے تھریڈز کے لیے، ڈیفالٹ سائز چھوٹا ہوتا ہے — 16–24 KB۔
کال اسٹیک (Call Stack) ایک LIFO (Last In, First Out) ڈیٹا ڈھانچہ ہے جو میتھڈ پر عملدرآمد کی ترتیب کا انتظام کرتا ہے۔ ہر بار جب پروگرام کسی میتھڈ کو کال کرتا ہے، JVM اسٹیک میں ایک فریم بناتا ہے اور اسے اوپر رکھتا ہے۔ جب میتھڈ مکمل ہوتا ہے، فریم ہٹا دیا جاتا ہے۔
ہر فریم میں شامل ہوتا ہے: اوپرینڈ اسٹیک (بائٹ کوڈ ہدایات کے لیے)، مقامی متغیرات کی صف (this سمیت)، مستقل پول کا حوالہ اور واپسی کا پتہ۔ میتھڈ میں جتنے زیادہ مقامی متغیرات ہوں گے، اس کے فریم کا سائز اتنا بڑا ہوگا اور اسٹیک بھرنے سے پہلے اتنے ہی کم میتھڈ کال کیے جا سکتے ہیں۔ 10 پیرامیٹرز اور 20 مقامی متغیرات والا میتھڈ بغیر پیرامیٹر والے میتھڈ کے مقابلے میں تقریباً 3 گنا زیادہ جگہ لیتا ہے۔
Android پر، ART ڈیسک ٹاپ JVM سے مختلف، اپنا اسٹیک نفاذ استعمال کرتا ہے۔ ART مخصوص حدود میں اسٹیک کو متحرک طور پر بڑھا سکتا ہے، لیکن ہر تھریڈ کے لیے ایک سخت حد اب بھی موجود ہے۔ مرکزی تھریڈ (UI تھریڈ) کا اسٹیک سب سے بڑا ہوتا ہے کیونکہ یہ پورے Activity لائف سائیکل اور ایونٹ پروسیسنگ کو سنبھالتا ہے۔
// ریکرشن جو StackOverflowError کا باعث بنتی ہے
fun recursiveCall(depth: Int): Int {
return recursiveCall(depth + 1) // کوئی بنیادی شرط نہیں
}
// کال ~1000 کی گہرائی پر StackOverflowError کا سبب بنے گی
recursiveCall(0)
پانچ عام منظرنامے موبائل ایپلیکیشنز میں StackOverflowError کا سبب بنتے ہیں۔ ان میں سے زیادہ تر ریکرشن سے متعلق ہیں، لیکن کم واضح وجوہات بھی ہیں۔
سب سے عام وجہ۔ ڈیولپر رکنے کی شرط کے بغیر یا ایسی شرط کے ساتھ ریکرسو میتھڈ لکھتا ہے جو کبھی true نہیں ہوتی۔ ہر کال ایک فریم شامل کرتی ہے، اور فریم کے سائز کے مطابق اسٹیک 500–2000 تکراروں میں بھر جاتا ہے۔ ایک عام مثال: n == 0 کی جانچ کیے بغیر n! فیکٹوریل کا حساب لگانا۔
ہر ریکرسو میتھڈ کے شروع میں بنیادی شرط چیک کریں۔ Kotlin میں، پیرامیٹر کی توثیق کے لیے require() یا check() استعمال کریں۔ گہری ریکرشن (100 سطحوں سے زیادہ) کے لیے، تکراری طریقہ کار سے تبدیل کرنے پر غور کریں۔
کلاس A B کی مثال بناتا ہے، کلاس B A کی مثال بناتا ہے — یہ کنسٹرکٹرز میں چکری انحصار ہے۔ A بنانے کی کوشش پر، B کا کنسٹرکٹر کال ہوتا ہے، جو A کے کنسٹرکٹر کو کال کرتا ہے، اور یوں StackOverflowError تک۔ DI فریم ورک (Dagger, Hilt) مرتب وقت پر ایسے چکروں کا پتہ لگاتے ہیں، لیکن دستی آبجیکٹ تخلیق انہیں نہیں پکڑتی۔
انحصاری گراف کے ساتھ ڈیپنڈنسی انجیکشن استعمال کریں: Dagger یا Koin تعمیر کے وقت چکروں کی جانچ کرتے ہیں۔ اگر چکر ناگزیر ہے تو، براہ راست انحصار کو سست ابتداء یا Provider فیکٹری والے انٹرفیس سے تبدیل کریں۔
// چکری انحصار — StackOverflowError
class A(private val b: B)
class B(private val a: A)
// سست حل
class A(private val bProvider: Provider<B>)
ریکرشن کے ذریعے View ٹری (ViewGroup.getChildAt())، فائل سسٹم یا JSON ڈھانچے کا ٹراورسل 500–1000 عناصر سے زیادہ کی گہرائی پر اسٹیک کی حد سے تجاوز کر سکتا ہے۔ 20 سطحوں کی نیسٹنگ والا Android ViewGroup نایاب ہے، لیکن 2000 نیسٹڈ آبجیکٹ والا ریکرسو JSON پارسنگ ایک حقیقی منظرنامہ ہے۔
ریکرسو ٹراورسل کو واضح Stack<T> یا ArrayDeque استعمال کرتے ہوئے تکراری ٹراورسل سے تبدیل کریں۔ یہ اسٹیک اوورفلو کے خطرے کو مکمل طور پر ختم کرتا ہے، کیونکہ heap آبجیکٹ اسٹیک کی حد سے محدود نہیں ہیں۔ Queue کے ذریعے BFS (چوڑائی سے پہلی تلاش) بھی مسئلہ حل کرتا ہے۔
Android-specific وجہ: کنفیگریشن کی غلط ہینڈلنگ پر لائف سائیکل میتھڈز کا چکری کال۔ مثال کے طور پر، onConfigurationChanged کے اندر recreate() کو کال کرنا، جو دوبارہ onConfigurationChanged کو کال کرتا ہے، اور یوں StackOverflowError تک۔ اسی طرح: onLayout() کے اندر setContentView()، جو ایک اور پیمائش اور ترتیب کو متحرک کرتا ہے۔
کنفیگریشن تبدیلیوں سے متعلق میتھڈز کے اندر recreate() کو کال نہ کریں۔ تھیم تبدیل کرتے وقت UI کو اپ ڈیٹ کرنے کے لیے، recreate کے بغیر setTheme() استعمال کریں۔ متحرک سمت تبدیلی کے لیے — کنفیگریشن میں فلیگ کے بغیر، requestOrientation() کو ایک بار کال کریں۔
Gson، Moshi یا Kotlin Serialization جب چکری حوالوں (A، B کا حوالہ دیتا ہے، B، A کا حوالہ دیتا ہے) والے آبجیکٹ کو سیریلائز کرنے کی کوشش کرتے ہیں تو لامتناہی ریکرشن میں چلے جاتے ہیں اور StackOverflowError سے کریش ہو جاتے ہیں۔ دو طرفہ تعلقات (JPA, Room with ForeignKey) والی Entity کو سیریلائز کرتے وقت یہ ایک عام مسئلہ ہے۔
چکر کے ایک طرف @Transient، @JsonIgnore یا @kotlinx.serialization.Transient استعمال کریں۔ Gson کے لیے — واضح گہرائی کی حد کے ساتھ JsonSerializer۔ Room کے لیے — Entity کو کبھی براہ راست سیریلائز نہ کریں، DTO میپر استعمال کریں۔
StackOverflowError کی تشخیص میموری کی دیگر خرابیوں سے آسان ہے: اسٹیک ٹریس زیادہ تر معاملات میں کال کا ایک دہرایا جانے والا سلسلہ دکھاتا ہے۔ یہ فوری طور پر ریکرشن کی طرف اشارہ کرتا ہے۔
StackOverflowError کا اسٹیک ٹریس منفرد ہے: پہلی 200–500 سطروں کے بعد، کال کا ایک ہی نمونہ دہرانا شروع ہو جاتا ہے۔ JVM آخر میں دہرائی جانے والی سطروں کو کاٹ دیتا ہے اور «... 1234 more» دکھاتا ہے۔ «...» سے پہلے غیر دہرائی جانے والی سطروں کی تعداد اس ریکرشن گہرائی کو ظاہر کرتی ہے جس نے خرابی پیدا کی۔
اسٹیک ٹریس کی پہلی سطریں پڑھیں — وہ ظاہر کرتی ہیں کہ کس میتھڈ سے دہراؤ شروع ہوا۔ وہ میتھڈ تلاش کریں جو خود کو کال کرتا ہے یا کال چین بناتا ہے جو اس پر واپس آتی ہے۔ بنیادی شرط درست کریں یا ریکرشن کو لوپ سے تبدیل کریں۔
عارضی طور پر، JVM فلیگ -Xss کے ذریعے اسٹیک سائز بڑھا کر مسئلہ حل کیا جا سکتا ہے۔ Android کے لیے، اسٹیک سائز AndroidManifest کے ذریعے مقرر کیا جاتا ہے: android:largeHeap اسٹیک کو متاثر نہیں کرتا۔ کوڈ میں تھریڈ اسٹیک بڑھانے کے لیے: Thread(ThreadGroup, Runnable, name, stackSize)۔ stackSize بائٹس میں مطلوبہ سائز ہے۔
// بڑھائے گئے اسٹیک کے ساتھ تھریڈ بنانا
val thread = Thread(null, runnable, "big-stack-thread", 64 * 1024)
thread.start()
اہم: اسٹیک بڑھانے سے مسئلہ حل نہیں ہوتا، صرف تاخیر ہوتی ہے۔ 10,000 سطحوں کی ریکرشن پر، 64 KB کا اسٹیک 128 KB کے اسٹیک سے بدل دیا جائے گا، جو 20,000 سطحیں دے گا — لیکن خرابی پھر بھی ہوگی، بس بعد میں۔ واحد صحیح حل ریکرشن کی تکراری تبدیلی ہے۔
تکراری الگورتھم درمیانی حالتیں ذخیرہ کرنے کے لیے کال اسٹیک استعمال نہیں کرتے — وہ انہیں heap میں ذخیرہ کرتے ہیں (Stack<T> یا ArrayDeque)۔ بائنری ٹری ٹراورسل، فیکٹوریل حساب، فبونیکی — کسی بھی ریکرشن کو واضح اسٹیک استعمال کرتے ہوئے تکرار میں تبدیل کیا جا سکتا ہے۔
// تکراری درخت ٹراورسل — StackOverflow کا کوئی خطرہ نہیں
fun traverseIterative(root: Node?) {
val stack = ArrayDeque<Node>()
stack.push(root)
while (stack.isNotEmpty()) {
val node = stack.pop() ?: continue
process(node)
node.right?.let { stack.push(it) }
node.left?.let { stack.push(it) }
}
}
StackOverflowError کی روک تھام قواعد اور اوزاروں کا ایک مجموعہ ہے جو ممکنہ ریکرسو چکروں کی پیداوار تک پہنچنے سے پہلے نشاندہی کرتا ہے۔
ڈیبگ بلڈز میں ریکرسو میتھڈز میں ایک حفاظتی گہرائی کاؤنٹر شامل کریں۔ اگر گہرائی ایک حد (مثلاً 1000) سے تجاوز کر جائے تو واضح پیغام کے ساتھ ایک استثنا پھینکیں۔ یہ ناقابل پڑھنے ٹریس والے StackOverflowError کو قابل فہم کاروباری استثنا میں بدل دیتا ہے۔
fun safeRecursive(n: Int, depth: Int = 0): Int {
if (depth > 1000) {
throw IllegalStateException("ریکرشن 1000 سطحوں سے تجاوز کر گئی")
}
return if (n <= 1) n
else safeRecursive(n - 1, depth + 1)
}
Detekt (Kotlin) اور Infer (Facebook) جامد تجزیہ کی سطح پر ممکنہ لامتناہی ریکرشنز تلاش کرتے ہیں۔ Detekt میں PotentiallyInfiniteRecursion اصول ہے جو پیرامیٹر تبدیل کیے بغیر خود کال کے بارے میں خبردار کرتا ہے۔ اسے CI اصولوں کے سیٹ میں فعال کریں اور شدت کو error پر سیٹ کریں۔
کوڈ کے جائزے میں، ان پر توجہ دیں: کوئی خود کال میتھڈ، لیمبڈا کے اندر ریکرسو کال (Kotlin ان لائن فنکشنز)، مختلف کلاسز کے درمیان چکری کال، پراپرٹی ڈیلیگیٹ میں ریکرشن۔ ہر ریکرسو میتھڈ کے لیے چیک کریں: کیا بنیادی شرط ہے، کیا پیرامیٹر ہر قدم پر بدلتا ہے، کیا پیرامیٹر کی تبدیلی بنیادی شرط تک پہنچنے کی ضمانت دیتی ہے۔
Kotlin tailrec موڈیفائر کو سپورٹ کرتا ہے: اگر کوئی ریکرسو میتھڈ tailrec سے نشان زد ہے اور کال دمی (آخری آپریشن) ہے تو مرتب اسے تکرار میں تبدیل کر دیتا ہے۔ تاہم، tailrec صرف خود کال (میتھڈ براہ راست خود کو کال کرتا ہے) کے لیے کام کرتا ہے، باہمی ریکرشن کے لیے کام نہیں کرتا، اور Android کے مطابق Kotlin ورژن 1.5 سے پہلے سپورٹ نہیں ہوتا۔
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n <= 1) acc
else factorial(n - 1, acc * n) // دمی کال
}
اکثر پوچھے گئے سوالات
ہاں، لیکن صرف Java کی سطح پر۔ Error، Exception کی طرح، Throwable ہے۔ تاہم، StackOverflowError کے بعد اسٹیک خراب ہو جاتا ہے — جو فریم فٹ نہیں ہو سکے وہ صحیح طریقے سے مکمل نہیں ہو سکتے۔ catch بلاک میں نیا آبجیکٹ بنانے کی کوشش دوسرا StackOverflowError پیدا کر سکتی ہے۔
مرکزی تھریڈ کے لیے — 32–48 KB، پس منظر کے تھریڈز کے لیے — 16–24 KB۔ صحیح سائز Android ورژن اور ڈیوائس بنانے والے پر منحصر ہے۔ ART متحرک اسٹیک توسیع استعمال کرتا ہے لیکن ابتدائی قدر سے 2× سے زیادہ نہیں۔
Kotlin میں — ہاں، اگر میتھڈ tailrec سے نشان زد ہے۔ مرتب دمی ریکرشن کو تکرار میں تبدیل کرتا ہے، اسٹیک کی نشوونما کو مکمل طور پر ختم کرتا ہے۔ Java میں، دمی ریکرشن JVM کے ذریعے بہتر نہیں کی جاتی (Scala جیسی فنکشنل زبانوں کے برعکس)۔
اسٹیک کا سائز ایمولیٹر اور حقیقی ڈیوائس پر مختلف ہو سکتا ہے۔ ایمولیٹر عام 512–1024 KB اسٹیک والی ڈیسک ٹاپ JVM استعمال کرتا ہے، جبکہ Android ART 32–48 KB استعمال کرتا ہے۔ خرابی ڈیسک ٹاپ JVM سے پہلے ART پر ظاہر ہوگی۔
میموری کا علاقہ: StackOverflowError اسٹیک خرابی (کال فریم) ہے، OutOfMemoryError heap خرابی (آبجیکٹ) ہے۔ StackOverflowError تقریباً ہمیشہ ریکرشن کی وجہ سے ہوتا ہے، جبکہ OutOfMemoryError میموری لیک یا بڑے آبجیکٹ کی وجہ سے ہوتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں