اگر در حین کدنویسی جاوا با ارور OutOfMemoryError مواجه شوید، به طور حتم دلیل آن چیزی نیست جز اینکه فضای مموری موجود پاسخگوی نیاز JVM نیست و یا اینکه فضای Heap پاسخگوی نیاز اپلیکیشن نبوده یا فضای PermGen یا Metaspace کافی نبوده و در نهایت یا اینکه JVM زمان زیادی را صرف Garbage Collection کرده است که برای فهمیدن این موضوع که دلیل رخ دادن این ارور دقیقاً کدام یک از موارد فوق است، فقط کافی است به پیام ارور ظاهر شده دقت کنید.
حجم مموری نیتیو پاسخگوی نیاز JVM نیست
مشاهدهٔ این ارور اساساً به این معنی است که بخشی از مموری که به ماشین مجازی جاوا (JVM) اختصاص داده شده است به طور کامل درگیر شده و هیچ قسمتی از آن آزاد باقی نمانده است.
در نسخهٔ ۳۲ بیتی جیویام چیزی در حدود ۳/۵ الی ۴ گیگابایت مموری برای انجام عملیات مورد نیاز در نظر گرفته شده است که اگر نیاز JVM از این مقدار فراتر رود، با ارور OutOfMemoryError مواجه خواهیم شد. حتی در نسخهٔ ۶۴ بیتی نیز ممکن است سیستمعامل مموری کافی برای اختصاص دادن به جیویام در اختیار نداشته باشد و با این ارور مواجه شویم (به عنوان مثال، در یک نوتبوک ۶۴ بیتی با سیستمعامل Mac OS X 10.11.6 و Java 1.8.1_112، پس از ۲۰۲۳ بار اجرای پیدرپی یک لوپ، ماشین مجازی جاوا دیگر قادر به اجرای مجدد آن نبوده و اروری به صورت زیر ایجاد خواهد شد (خطای ناشی از کمبود حجم مموری نیتیو، همواره چیزی مشابه متن خطای زیر است):
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
فضای Heap پاسخگوی نیاز اپلیکیشن نیست
Heap فضایی از مموری است که تمام آبجکتهای ایجاد شده در حین اجرای یک برنامهٔ جاوا در آن ذخیره میشوند (لازم به ذکر است آبجکتهایی که تولید شده و در جایگاه خود مورد استفاده قرار گرفته و سپس بدون کاربرد باقی ماندهاند در طی فرآیندی به نام Garbage Collection از فضای Heap حذف میشوند تا فضا برای نگهداری آبجکتهای جدید مورد استفاده قرار گیرد.)
اگر خطای OutOfMemoryError به دلیل کافی نبودن فضای Heap رخ دهد، از آنجا که عبارت Java Heap Space در متن پیام خطا درج میشود، تشخیص دلیل آن بسیار ساده خواهد بود که در چنین مواردی افزایش سایز Heap میتواند راهحل خوبی باشد اما اگر سیستم با کمبود مموری مواجه باشد، این راهحل موقتی بوده و تنها میتواند رخ دادن ارور OutOfMemoryError را به تأخیر بیندازد و در نهایت خطایی بهصورت زیر ظاهر خواهد شد:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
فضای PermGen یا Metaspace کافی نیست
Permanent Generation که به طور خلاصه PermGen نامیده میشود، بخشی از مموری است که اطلاعات کلاسهای بارگذاری شده در حین اجرای یک برنامهٔ جاوا و همچنین فیچرهای پیشرفتهای همچون String Pool را در خود نگاه میدارد. به طور کلی، PermGen در نسخهٔ ۷ زبان جاوا و همچنین نسخههای قدیمیتر حجم محدودی دارد و این بدان معنا است که اگر کلاسهای متعدد و بسیار زیادی را در برنامهٔ خود ایجاد نموده باشید، فضای PermGen به زودی پر خواهد شد و خطای OutOfMemoryError رخ خواهد داد که در اینگونه موارد، افزایش حجم PermGen میتواند مشکل را حل کند.
در نسخهٔ ۸ این زبان، به جای PermGen فضایی به نام Metaspace در نظر گرفته شده است که حجم آن به طور پیشفرض و تا زمانی که حدی برای آن تعیین نشود نامحدود است؛ بنابراین منجر به ارور OutOfMemoryError نخواهد شد. به طور کلی، برای تشخیص اینکه این ارور به دلیل کافی نبودن فضای PermGen یا Metaspace رخ داده است یا خیر، باید به متن پیام ارور دقت کنید که چیزی شبیه به متن زیر خواهد بود:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
JVM زمان زیادی را صرف Garbage collection مینماید
Garbage Collection یا بهطور خلاصه GC فرآیندی است که در طی آن آبجکتهایی که دیگر کاربردی در اپلیکیشن ندارند از چرخهٔ اجرا خارج و حذف میشوند و بدین ترتیب مموری موجود برای اجرای سایر قسمتهای اپلیکیشن آزاد میشود. یکی از دلایل ایجاد OutOfMemoryError این است که جیویام زمان زیادی را صرف GC مینماید که این مورد از بقیهٔ موارد پیچیدهتر بوده و از هممین روی برطرف کردن آن نیز دشوارتر است. اساساً این وضعیت زمانی ایحاد میشود که تمامی شروط زیر برقرار باشند:
- JVM بیش از ۹۸٪ از زمان پردازش خود را صرف GC نماید (۹۸٪ مقدار پیشفرض است و با تنظیم GCTimeLimit=N میتوان محدودیت این مقدار پیشفرض را لغو نموده و آن را نامحدود کرد.)
- در این پروسه کمتر از ۲٪ از فضای Heap آزاد باقی بماند (۲٪ مقدار پیشفرض است و با تنظیم GCHeapLimit=N میتوان این محدودیت را از میان برداشت.)
- دو شرط فوق در مورد چندین سیکل Full GC پیدرپی صادق باشد.
- گزینهٔ UseGCOverheadLimit غیرفعال نشده باشد (این گزینه به طور پیشفرض فعال است.)
به هر حال، ادامهٔ یافتن سیکل Full GC برای مدت طولانی، به معنای کافی نبودن مموری است که اگر ماشین مجازی جاوا ۹۸٪ از زمان خود را صرف آزاد کردن تنها ۲٪ از مموری Heap میکند، این بدان معنا است که سیپییو تقریباً به طور کامل درگیر گاربج کالکشن بوده و عملاً ادامهٔ اجرای اپلیکیشن بیمعنا است و هیچ جای تعجب نیست که در چنین شرایطی جیویام اجرای کدها را متوقف نموده و ارور OutOfMemoryError ظاهر شود که در اینگونه موارد متن پیام ارور به صورت زیر خواهد بود:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
اگر تاکنون با چنین مشکلی در حین کدنویسی اپلیکیشنهای جاوا مواجه شدهاید، در چنین شرایطی چگونه مشکل OutOfMemoryError را حل نمودهاید؟