Programming Basics SQL HTML CSS JavaScript Python C++ Java JavaFX Swing Problem Solving English English Conversations Computer Fundamentals Learn Typing

تحسين إستهلاك الذاكرة RAM لبرامج جافا

  • 1- تحويل المتغيرات العامة لمتغيرات محليّة
  • 2- إستخدم كلمة new بحرص
  • 3- النصوص المتكررة
  • 4- إستخدم أفضل خوارزمية
  • 5- حل مشكلة تسرب الذاكرة (Memory Leaks)

تتميز لغة جافا بأنها تتعامل مع آلة جافا الافتراضية JVM بدلاً من نظام التشغيل مما يسمح لها بأن تكون عابرة للمنصات Cross-Platform, و تكون آلة جافا الإفتراضية هي المسؤولة عن التعامل مع نظام التشغيل و تنفيذ أوامر لغة , و من ذلك تخزين قيم المتغيرات في الذاكرة RAM و إدارتها, وبما أن لغة جافا أتت لتسهّل البرمجة و تركز تفكير المبرمج على الخوارزمية فقط فقد تم حذف إمكانية إزالة المتغيرات من الذاكرة RAM الموجودة في لغة ++C و إستبدالها بجامع القمامة Garbage Collector.

سنتحدث في هذا المقال عن عدة طرق لتحسين إستهلاك برامج جافا للذاكرة RAM.

1- تحويل المتغيرات العامة لمتغيرات محليّة

المتغيرات العامة هي المتغيرات التي يتم تعريفها داخل الكلاس (Class), أما المتغيرات المحلية هي المتغيرات المعرفة داخل الدوال (Methods), و تتميز المتغيرات العامة بأن قيمها تبقى في الذاكرة ما دام الكائن الموجودة فيه حياً, أما المتغيرات المحلية فيتم إسناد القيم إليها خلال تنفيذ الدالة, و يتم إزالة البيانات البدائية (Primitive Data) و المراجع (References) من الذاكرة فور إنتهاء تنفيذ الدالة, و إزالة المراجع للكائنات بشكل لا يمكن البرنامج من إستخدامها في المستقبل سيسمح لجامع القمامة Garbage Collector بإزالة ذلك الكائن من الذاكرة Heap عندما يتم تحفيزه.

public class Veriables{
private String instanceVariable = "هذا متغير عام";
private void method(){
String localVariable = "هذا متغير محلي";
}
}
public class Veriables{ private String instanceVariable = "هذا متغير عام"; private void method(){ String localVariable = "هذا متغير محلي"; } }

2- إستخدم كلمة new بحرص

تستخدم الكلمة المفتاحية new لإنشاء الكائنات أو المصفوفات و حجز أماكن لها في الذاكرة Heap، و في أحيان كثيرة يحتاج المبرمج لإنشاء الكثير من الكائنات التي تتبع لنفس الكلاس داخل الدوال لإنجاز العديد من المهمات, و عندها ينصح المبرمج إن كان إنشاء الكائن مكلفاً بإعادة إستخدام الكائن نفسه عدة مرات عوضًا عن إنشاء كائن لكل مهمة رغم أن ذلك قد يصعب قراءة الكود، و عند الإنتهاء من الكائنات التي تحجز مساحة كبيرة في الذاكرة مثل النصوص المقروءة من الملفات أو مصفوفة byte ينصح أيضاً بإزالة المراجع لتلك الكائنات عبر إسناد nullللمتغير الذي يحفظ المراجع, و هذا يسمح لجامع القمامة في المستقبل عند إيقافه عمل البرنامج مؤقتاً لجمع القمامة بإزالة تلك الكائنات من الذاكرة Heap.

3- النصوص المتكررة

النص String هو مصفوفة من الأحرف (Characters) و التي يستهلك أي منها 2Bytes, و تكرار إنشاء النصوص المتشابهة يستهلك من الذاكرة لحجز مساحة لكل نص, لذلك ظهرت مشكلة النصوص المكررة (Duplicated Strings)، و لحل ذلك قام مطورو جافا بتطوير المترجم (Compiler) لكشف النصوص المتشابهة المكتوبة مباشرة في الكود و تعويضها بمرجع واحد, و المثال التالي يوضح ذلك:

public class DuplicatedStrings {
public static void main(String[] args) {
String text = "Hello";
System.out.println(text == "Hello"); // => true
}
}
public class DuplicatedStrings { public static void main(String[] args) { String text = "Hello"; System.out.println(text == "Hello"); // => true } }

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

الطريقة السابقة فعالة فقط للنصوص المكتوبة مباشرةً في الكود, أما النصوص التي يتم إنشاؤها أثناء عمل البرنامج (At Runtime) فلا يتم إختصار المكرر منها بكائن واحد, لذلك توفر لغة جافا الدالة intern() في الكلاس String. هذه الدالة تقوم بالبحث في مجمع النصوص (String Pool) عن نص مشابه للنص المنفّذ عليه هذه الدالة و تعيده, و إن لم تجد ما يشابهه في المجمع تقوم بوضع النص في المجمع, و مجمع النصوص هو مجمع يحوي على نصوص غير متشابهة, و عندما يحتاج المبرمج للبحث عن مرجع لنص متكرر في برنامجه, فعوضاً عن إنشاء كائن لنص جديد يمكنه البحث في المجمع عن نص مخزن مسبقاً و هكذا يختصر النصوص المكررة بكائن واحد.

المثال التالي يوضح طريقة عمل المجمع.

public class StringPoolExample {
public static void main(String[] args) {
String x = "Hello";
String y = new String("Hello"); //if we didn't use new keyword the compiler will give y a reference to x
String z = x.intern(); //string pool will save x reference and will return it since there is no renference for Hello string in the pool
String i = y.intren(); //string pool will see that there is a "Hello" string in the pool with a reference to x and will return it
System.out.println(x==y); // => false
System.out.println(x==z); // => true
System.out.println(x==i); // => true
System.out.println(i==y); // => false
}
}
public class StringPoolExample { public static void main(String[] args) { String x = "Hello"; String y = new String("Hello"); //if we didn't use new keyword the compiler will give y a reference to x String z = x.intern(); //string pool will save x reference and will return it since there is no renference for Hello string in the pool String i = y.intren(); //string pool will see that there is a "Hello" string in the pool with a reference to x and will return it System.out.println(x==y); // => false System.out.println(x==z); // => true System.out.println(x==i); // => true System.out.println(i==y); // => false } }

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

public class StudentNames {
private static ArrayList<:String> studentsNames = new ArrayList<>();
public static void main(String[] args) {
System.out.println("Please write the student names");
while(true) {
String input = System.console().readLine();
if (input.equals("print names")
break;
studentNames.add(input.intern());
}
System.out.println(studentNames);
}
}
public class StudentNames { private static ArrayList<:String> studentsNames = new ArrayList<>(); public static void main(String[] args) { System.out.println("Please write the student names"); while(true) { String input = System.console().readLine(); if (input.equals("print names") break; studentNames.add(input.intern()); } System.out.println(studentNames); } }

4- إستخدم أفضل خوارزمية

يمكن الوصول للحل بخوارزميات عديدة, منها ما تتنفذ بسرعة و أخرى تحجز مساحة صغيرة للتخزين , فعليك إنتقاء أفضل خوارزمية لتحسين إستهلاك موارد الحاسوب و منها الذاكرة RAM, فمثلاً إذا قمنا بإجراء مقارنة بين ArrayList و LinkedList فسنلاحظ أن LinkedListتحيط كل كائن بعقدة Node تحوي على مرجع للكائن السابق و مرجع الكائن التالي, و هذا يستهلك مقداراً أكبر من الذاكرة RAM لكل كائن يتم حفظه في القائمة بالنسبة لـ ArrayList التي تقوم بحفظ الكائنات مباشرة في مصفوفة.

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

5- حل مشكلة تسرب الذاكرة (Memory Leaks)

عند برمجة البرامج التي تعمل لأوقات طويلة قد يلاحظ المبرمج ازدياداً مطرداً في مقدار إستهلاك البرنامج للذاكرة RAM رغم عدم وجود سبب واضح لتلك الزيادة, تسمى هذه الحالة تسرّب الذاكرة, و يكون السبب عادةً أن هناك كائنات موجودة في الذاكرة Heap لا يمكن لجامع القمامة إزالتها, و ذلك بسبب أن البرنامج يملك مرجعاً لتلك الكائنات فتتكدس في الذاكرة, لذلك ينصح المبرمجين بمراجعة خوارزميتهم و إستخدام برامج تحليل لمعرفة من يقوم بإستهلاك الذاكرة مثل Netbeans Profiler و JProfiler.

آخر تحديث في 06-01-2024

الكاتب

رامي عبدالله

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

تعليقات

لا يوجد أي تعليق بعد

أضف تعليق

يجب تسجيل الدخول حتى تتمكن من إضافة تعليق أو رد.