تسببت عملية استيراد أيقونة واحدة ديناميكية في انهيار خادم التطوير على جهاز يعمل بنظام Windows Subsystem for Linux 2 (WSL2) بذاكرة 16 جيجابايت، مما أجبر عملية vmmemWSL على استهلاك كل الذاكرة المتاحة وتجميد نافذة Linux بالكامل. حدث الانهيار أثناء تشغيل مشروع Next.js 16 يستخدم Turbopack، وحدث ذلك رغم وجود ملف .wslconfig الذي يضع حداً أقصى لاستخدام ذاكرة الوصول العشوائي (RAM) للآلة الافتراضية.
لماذا يمكن لعملية استيراد صغيرة أن تتحول إلى وحش
حاول المطور استدعاء الأيقونات أثناء وقت التشغيل (runtime) باستخدام نقطة دخول ديناميكية (dynamic entry point). أدت عملية الاستيراد إلى جلب حزمة أيقونات تحتوي على ما يقرب من 9,000 وحدة (module). يقوم Turbopack، وهو أداة التجميع (bundler) القائمة على لغة Rust والتي تشغل خادم تطوير Next.js 16، ببناء خريطة وحدات (module map) كاملة لكل حزمة يلمسها. في وضع التطوير، تعيش هذه الخريطة في الذاكرة وتتحدث مع كل تغيير في الملفات. أدى تحميل حزمة الأيقونات بالكامل إلى إجبار Turbopack على تخصيص كمية كبيرة من الذاكرة (RAM)، مما أدى بسرعة إلى الوصول إلى الحد الأقصى الصارم الذي وضعه ملف .wslconfig. وبمجرد الوصول إلى الحد، توقفت الآلة الافتراضية لـ WSL عن الاستجابة؛ ولم يفلح اختصار Ctrl + C في أي شيء، وكان الحل الوحيد هو إغلاق نظام Windows المضيف إجبارياً.
نجحت عملية بناء الإنتاج (production build) لنفس الكود لأن أداة التجميع تقوم بتجميع الرسم البياني (graph) مرة واحدة، وتصدر الأصول (assets)، ثم تغلق. أما خادم التطوير، فيحتفظ بالرسم البياني في الذاكرة لتمكين ميزة التحديث السريع (hot-reloading). لذا، فإن نجاح عملية البناء (green build) لا يضمن قدرة بيئة التطوير على الصمود أمام نفس نمط الاستيراد.
المخاطر الأوسع نطاقاً
يعتمد المطورون الذين يعملون على سلاسل أدوات (toolchains) قائمة على Linux داخل نظام Windows على WSL2 لعزل استخدام الموارد. عندما تستهلك عملية استيراد واحدة ذاكرة الآلة الافتراضية، يمكن أن يصبح النظام المضيف بأكمله بطيئاً أو غير مستجيب، مما يؤثر على أي حاويات (containers) أو تطبيقات أخرى على نفس الجهاز. كما يسلط هذا الحادث الضوء على عدم التوافق بين أعلام ضبط الذاكرة (memory-tuning flags) المعتادة في Node.js وبنية Turbopack: حيث يعمل Turbopack بلغة Rust وليس V8، لذا فإن زيادة علم Node --max-old-space-size لا تفعل شيئاً للحد من استهلاكه للذاكرة.
ما الذي حدث فعلياً
- نقطة دخول ديناميكية: طلبت عبارة الاستيراد (import statement) من أداة التجميع معاملة حزمة الأيقونات بأكملها كوحدة واحدة يتم تحميلها بكسل (lazily-loaded). رد Turbopack بتحليل كل ملف بشكل استباقي (eagerly parsing) لبناء خريطة الوحدات.
- الرسم البياني لخادم التطوير: على عكس عملية تجميع الإنتاج التي تتم لمرة واحدة، يحتفظ خادم التطوير بالرسم البياني الكامل للاعتماديات في الذاكرة لتوفير استجابة فورية لتغييرات الملفات.
- الحد الأقصى للذاكرة: حدد ملف .wslconfig الآلة الافتراضية بجزء بسيط من ذاكرة النظام المضيف. وعندما تجاوز طلب Turbopack هذا الحد، تجمدت الآلة الافتراضية.
حلول قللت استهلاك الذاكرة إلى النصف
طبق المطور ثلاثة تغييرات عملية قللت استخدام الذاكرة من 3.6 جيجابايت إلى 1.87 جيجابايت واستعادت استقرار النظام:
- التوقف عن استخدام نقاط الدخول الديناميكية للأصول الصغيرة – استورد الأيقونات التي تحتاجها فقط، مثل
import { SearchIcon } from 'icon-pack/search'. إذا كنت تحتاج إلى عدد قليل جداً، فإن ملفات SVG المضمنة (inline SVGs) تكون أخف وزناً. - تفعيل
optimizePackageImportsفيnext.config.ts– يخبر هذا الخيار Turbopack بتوجيه عمليات الاستيراد إلى مسارات الملفات المحددة بدلاً من جذر الحزمة، مما يمنعه من تحميل شجرة الحزمة بأكملها. - عدم الاعتماد على أعلام الذاكرة (heap flags) الخاصة بـ Node – بما أن استهلاك Turbopack للذاكرة محكوم ببيئة تشغيل Rust الخاصة به، فإن
--max-old-space-sizeليس له أي تأثير على المشكلة.
لا يزال من المستحسن الإبقاء على قيود .wslconfig كما هي. فقد يتسبب الحد الصارم في إيقاف الآلة الافتراضية مؤقتاً بدلاً من تسببها في انهيار نظام Windows المضيف، مما يمنحك فرصة للتدخل قبل توقف كل شيء.
ما يجب مراقبته في سير عملك الخاص
- لوحات مراقبة الذاكرة: يمكن لأدوات مثل
htopداخل WSL أو مدير مهام Windows (Windows Task Manager) الكشف عن وقت ارتفاع استهلاك vmmemWSL. قم بضبط تنبيهات إذا اقترب الاستخدام من الحد الذي قمت بتكوينه. - تدقيق حجم الحزم: قبل إضافة أي مكتبة، تحقق من عدد الوحدات التي تحتوي عليها. يمكن لحزم الأيقونات الكبيرة، أو مجموعات الأدوات المساعدة، أو مكتبات المكونات أن تضخم الرسم البياني للتطوير بصمت.
- الاستيرادات الانتقائية: فضل استخدام الاستيرادات المسماة (named imports) أو مسارات الملفات المباشرة بدلاً من الاستيرادات العامة (wildcard) أو الديناميكية، خاصة في بيئة التطوير حيث تحتفظ أداة التجميع بكل شيء في الذاكرة.
- التماثل بين الإنتاج والتطوير: تعامل مع نجاح عملية بناء الإنتاج كخطوة تحقق منفصلة. قم بتشغيل خادم التطوير مع مراقب للذاكرة لالتقاط المشكلات التي تظهر فقط أثناء التحديث السريع (hot-reloading).
الخلاصة
يمكن لحزمة أيقونات واحدة مستوردة ديناميكياً أن تستهلك ذاكرة كافية لشل حركة خادم تطوير Next.js القائم على WSL2، حتى عندما تكون موارد المضيف محدودة عمداً. من خلال تجنب الاستيرادات الديناميكية الواسعة، وتفعيل تحسين الاستيراد على مستوى الحزمة، ومراقبة استخدام الذاكرة، يمكن للمطورين الحفاظ على استجابة بيئات Linux داخل Windows وتجنب الإغلاق الإجباري للنظام.
