ده خطای رایج عمده مربوط به مشکلات کارایی در جاوا
کارایی یا Performance در جاوا مسالهای است که همه توسعهدهندگان برنامههای کاربردی جاوا درگیر آن هستند. چرا که ساختن برنامهای سریع همانقدر مهم است که ساختن برنامهای کاربردی اهمیت دارد.
Steven Haines۱ از تجربیات شخصی خود در این زمینه صحبت میکند. وی مشکلات مربوط به کارایی را در سه دسته عمده قرار میدهد:
- مشکلات پایگاه داده
- مشکلات حافظه
- مشکلات همروندی و اجرای موازی
بیایید هر دسته را بررسی کنیم:
پایگاه داده
از آن جایی که پایگاه داده مولفه پایه هر برنامه کاربردی است، یکی از ریشههای اساسی مسائل مربوط به کارایی است. مشکلات ممکن است به خاطر استفاده ناصحیح برای دسترسی به پایگاه داده، سایز نامناسب استخر اتصالات یا عدم تنظیمات مناسب رخ دهند.
- پیکربندی بادوام (persistence configuration)
هرچند امروزه Hibernate و دیگر پیادهسازیهای JPA تنظیمات دقیقی برای دسترسی به پایگاه داده فراهم میآورند، اما هنوز گزینههایی مثل eager fetching یا lazy fetching وجود دارد که میتواند زمان پاسخ و سربار پایگاه داده را بالا برد. Eager fetching فراخوانیهای کمتر اما پیچیدهتری به پایگاهداده دارد، در مقابل lazy fetching فراخوانیهای بیشتر اما سادهتر و سریعتری خواهد داشت.
مشکل زمانی رخ میدهد که بارکاری برنامه و در نتیجه حجم دسترسی به پایگاه داده افزایش پیدا میکند. پس برای اصلاح این مشکل، میتوان به شمارنده تعاملات تجاری (business interaction counter)، شمارنده پایگاه داده، توجه کرد. برای اجتناب از وقوع چنین مشکلاتی شما باید دوام تکنولوژی مورد استفاده را بفهمید، تمام گزینههای پیکربندی را به درستی تنظیم کنید و قابلیتهای برنامه خود را با نیازهای حوزه مساله هماهنگ سازید.
- ذخیرهساز (cache)
caching و ذخیرهسازی کارایی برنامهها را بهینه ساخته است، چرا که دسترسی به داده درون حافظه سریعتر از دسترسی به داده مانا مثلا روی دیسک میباشد. مشکلات وقتی بروز میکند که هیچ caching استفاده نمیشود پس هر زمان به منبعی نیاز بود از پایگاه داده گرفته میشود. البته زمانی هم که caching استفاده میشود مشکلاتی به دلیل پیکربندی نامناسب آن میتواند رخ دهد. اشیا cache شده برخلاف poolها دارای حالت هستند. پس لازم است که به خوبی پیکربندی شده باشد تا حافظه را هدر ندهد. اما اگر یک شئ حذف شده مجددا درخواست شود چه میشود؟ این نسبت ‘miss’ ها در تنظیمات cache بایستی علاوه بر حافظه به درستی انتخاب شود.
caching توزیع شده نیز میتواند مشکلزا باشد. همگامسازی(synchronization) در این شرایط الزامی خواهد بود. پس یک بروزرسانی در cache به همهی cacheها و سرورها منتشر میشود. که فرایندی هزینهبر است.
در صورتی که از cache به درستی استفاده شده باشد با افزایش بار کاری برنامه کاربردی، حجم کار پایگاه داده افزایش نمییابد اما در غیر اینصورت میتواند منجر به سربار cpu یا حتی نرخ دسترسی به دیسک شود.
- استخر اتصالات
ساخت استخر اتصالات خیلی هزینه بر است، به همین دلیل معمولا قبل از شروع کار برنامه، ساخته میشوند. یک استخر اتصالات بین تعاملات مختلف به اشتراک گذاشته میشود و سایز آن بارکاری پایگاه داده را محدود میکند.
سایز استخر خیلی مهم است. اتصالات بسیار زیاد منجر به افزایش زمان پاسخ و بار کاری پایگاه داده میشود. برای حل این مشکل لازم است بفهمید که برنامه شما منتظر یک اتصال جدید است یا یک کوئری پایگاه داده. البته میتوان با امتحان کردن سایزهای مختلف استخر، بهترین سایز برای بهینه کردن کار پایگاه داده را انتخاب کرد.
حافظه
مشکلات مربوط به حافظه مرتبط به زبالهروب و نشتی حافظه هستند.
- زباله روب
زباله روب باعث میشود تمامی ریسهها برای آزادسازی حافظه متوقف شوند. وقتی این کار زمان زیادی ببرد یا به دفعات تکرار شود میتواند مسالهساز باشد. از علائم اولیه آن زمان پاسخ بالا و تغییرات ناگهانی مصرف cpu شود. برای حل آن، میتوانید پارامتر -verbosegc را تنظیم کرده و از یک ابزار نظارت بر کارایی برنامه، برای مشاهده زمانهای عمده اجرای زبالهروب و ابزاری برای نظارت بر استفاده از هیپ و cpu استفاده کنید. این اتفاق اجتناب ناپذیر است اما با تنظیم سایز هیپ میتوان آن را محدود کرد.
- نشتی حافظه
نشتی حافظه در جاوا با سی و سی++ که بیشتر مربوط به مسائل مدیریت ارجاعات هستن، متفاوت است. در جاوا ارجاع به یک شئ حتی اگر دیگر مورد استفاده قرار نگیرد، حفظ و نگهداری میشود. این مساله میتواند خطای outOfMemory پدید آورده و نیاز به اجرای مجدد JVM شود. وقتی مصرف حافظه افزایش پیدا کند و هیپ با کمبود حافظه روبرو شود، نشتی حافظه رخ داده است برای حل آن میتوانید پارامترهای JVM را به درستی پیکربندی کنید. برای اجتناب از نشتی حافظه، بایستی حین کد زدن به آن توجه کنید. – کالکشنهای حساس جاوا، یا مدیریت session- البته میتوانید از یک متخصص بخواهید کد شما را بررسی کند و از ابزارهایی برای اجتناب از نشتی حافظه و تحلیل هیپ استفاده کنید.
همروندی:
همروندی وقتی رخ میدهد که چندین محاسبه در یک زمان اجرا شوند. جاوا از همگامسازی و قفلها برای مدیریت چندریسهای استفاده میکند که می تواند منجر به deadlock، gridlock شود و سایز استخر ریسهها مسالهساز خواهد بود.
- deadlock
این اتفاق زمانی رخ میدهد که دو یا چند ریسه منتظر منبع مشترکی هستند و اولی منتظر دیگری است تا منبعی را آزاد کند و بالعکس. وقتی deadlock رخ میدهد، JVM تمام ریسهها را مصرف میکند و برنامه کندتر میشود. deadlockها خیلی به سختی تکثیر میشوند و راه حل مشکل deadlockها گرفتن یک dump از ریسه زمانی است که دو ریسه deadlock شده اند و بررسی trace پشته آنهاست. برای اجتناب از چنین مسالهای بهتر است که برنامه و منابع آن را تا جای ممکن با استفاده از سنکرونها و بررسی تعاملات ریسهها تغییرناپذیر کنید.
- Gridlock
این اتفاق زمانی رخ میدهد که از همگامسازی (synchronization) بیش از حد استفاده شده باشد و زمان زیادی صرف انتظار برای دسترسی به یک منبع میشود. در این شرایط زمان پاسخ افزایش و کارایی cpu کاهش پیدا میکند. برای حل مشکل بعد از اینکه بررسی نمودید کجا و به چه دلیل ریسهها منتظر هستند، همگامسازیهای اضافی را حذف کنید.
- Thread pool configuration locks
وقتی یک برنامه از یک سرور کاربردی یا web container استفاده میکند، یک استخر از ریسهها برای کنترل همروندی درخواستها استفاده میشود. اگر این استخر خیلی کوچک باشد، درخواستها مدت زمان زیادی را باید منتظر بمانند و اگر خیلی بزرگ باشد منابع پردازشگر خیلی مشغول خواهند شد. این مشکل با چک کردن utilization استخر ریسهها و همینطور cpu قابل کشف است و میتوان تصمیم گرفت که سایز این استخر باید کاهش یا افزایش پیدا کند.
منابع:
https://www.javacodegeeks.com/2015/02/top-10-common-java-performance-problems.html
http://info.appdynamics.com/rs/appdynamics/images/Top_10_Java_Performance_Problems_eBook.pdf
۱ Steven Haines is a technical architect at Kit Digital, working onsite at Disney in both an advisory role to the performance of Disney’s largest software endeavor to date as well as managing the development, quality assurance, and business analysis teams responsible for the ticketing and yet undisclosed systems. He has written three books: Java 2 From Scratch, Java 2 Primer Plus, and Pro Java EE Performance Management and Optimization, and has spent the better part of the past 14 years in the Application Performance Management (APM) space at both Quest Software and AppDynamics. Additionally he taught Java at both Learning Tree University and the University of California Irvine.