يعد تخصيص وإدارة ذاكرة Java أحد التقنيات الأساسية لـ Java. لقد قدمنا سابقًا معرفة إدارة الذاكرة في Java ، وتسرب الذاكرة ، ومجموعة Java Garbage. اليوم ، سوف نذهب مرة أخرى في عمق جافا الأساسية ونقدم بالتفصيل معرفة جافا في تخصيص الذاكرة. بشكل عام ، ستشمل Java المجالات التالية عند تخصيص الذاكرة:
◆ التسجيل: لا يمكننا التحكم فيه في البرنامج
◆ المكدس: يخزن الأنواع الأساسية من البيانات والمراجع إلى الكائنات ، ولكن الكائن نفسه غير مخزن في المكدس ، ولكن يتم تخزينه في الكومة (الكائن الذي يخرج من الجديد)
◆ كومة: تخزين البيانات التي تم إنشاؤها باستخدام جديد
◆ المجال الثابت: أعضاء ثابتون مخزّن في كائن محدد بستاتيك
◆ حمام سباحة ثابت: ثوابت المتجر
◆ تخزين غير رام: مساحة تخزين دائمة مثل القرص الصلب
مكدس في تخصيص ذاكرة جافا
يتم تخصيص بعض الأنواع الأساسية للبيانات المتغيرة المحددة في الوظيفة والمتغيرات المرجعية للكائن في ذاكرة مكدس الوظيفة.
عندما يتم تعريف متغير في كتلة من التعليمات البرمجية ، يخصص Java مساحة الذاكرة للمتغير في المكدس. عندما يخرج المتغير من النطاق ، ستقوم Java تلقائيًا بتقديم مساحة الذاكرة المخصصة للمتغير ، ويمكن استخدام مساحة الذاكرة على الفور على الفور. حجم البيانات ودورة الحياة في المكدس مؤكد ، وتختفي هذه البيانات عندما لا تشير أي مرجع إلى البيانات.
كومة في تخصيص ذاكرة جافا
يتم استخدام ذاكرة الكومة لتخزين الكائنات والصفائف التي تم إنشاؤها بواسطة New. تتم إدارة الذاكرة المخصصة في الكومة بواسطة جامع القمامة التلقائي في الجهاز الافتراضي Java.
بعد إنشاء صفيف أو كائن في الكومة ، يمكن تعريف متغير خاص في المكدس ، بحيث تكون قيمة هذا المتغير في المكدس تساوي العنوان الأول للمصفوفة أو الكائن في ذاكرة الكومة ، ويصبح المتغير في المكدس متغير مرجعي للمصفوفة أو الكائن. المتغير المرجعي يعادل اسمًا مُعطى لصفيف أو كائن. يمكنك استخدام المتغيرات المرجعية في المكدس في البرنامج للوصول إلى الصفيف أو الكائن في الكومة. المتغير المرجعي يعادل اسمًا مُعطى لصفيف أو كائن.
المتغيرات المرجعية هي متغيرات عادية ، والتي يتم تعيينها على المكدس عند تحديدها. يتم إصدار المتغيرات المرجعية بعد تشغيل البرنامج خارج نطاقه. يتم تخصيص المصفوفة والكائن نفسها في الكومة. حتى إذا تم تشغيل البرنامج خارج كتلة التعليمات البرمجية حيث توجد البيانات التي تستخدم جديدة لإنشاء الصفيف أو الكائن ، فلن يتم إصدار الذاكرة التي تشغلها الصفيف ولن يتم إصدار الكائن نفسه. يصبح الصفيف والكائن فقط القمامة عندما لا يكون هناك متغير مرجعي يشير إليه ، ولا يمكن استخدامه ، ولكن لا يزال يشغل مساحة الذاكرة. يتم جمعه (تم إصداره) من قبل جامع القمامة في وقت غير مؤكد. هذا هو أيضًا السبب وراء قيام جافا بمزيد من الذاكرة.
في الواقع ، تشير المتغيرات في المكدس إلى المتغيرات في ذاكرة الكومة ، وهي المؤشر في Java!
كومة ومكدس
كومة Java هي منطقة بيانات وقت التشغيل ، تخصص منها الكائنات مساحة. يتم إنشاء هذه الكائنات من خلال تعليمات مثل New و Newarray و anewarray و multianewarray. لا تتطلب إصدار رمز البرنامج بشكل صريح. الكومة مسؤولة عن جمع القمامة. تتمثل ميزة الكومة في أنه يمكنه تخصيص حجم الذاكرة ديناميكيًا ، ولا يجب إخبار العمر بالمترجم مقدمًا ، لأنه يخصص الذاكرة ديناميكيًا في وقت التشغيل. سيقوم جامع القمامة من Java تلقائيًا بجمع البيانات التي لم تعد تستخدم. لكن العيب هو أنه لأنه يحتاج إلى تخصيص الذاكرة ديناميكيًا في وقت التشغيل ، فإن سرعة الوصول أبطأ.
تتمثل ميزة المكدس في أن سرعة الوصول أسرع من الكومة ، والثانية فقط إلى السجلات ، ويمكن مشاركة بيانات المكدس. لكن العيب هو أن حجم البيانات وعمره في المكدس يجب أن يكون حتميًا ويفتقر إلى المرونة. يخزن المكدس بشكل أساسي بعض الأنواع الأساسية من البيانات المتغيرة (int ، قصيرة ، طويلة ، بايت ، تعويم ، مزدوج ، منطقية ، شار) ومقابض الكائن (المراجع).
ميزة خاصة مهمة للغاية للمكدس هي أنه يمكن مشاركة البيانات الموجودة في المكدس. لنفترض أننا نحدد في نفس الوقت:
كود جافا
int a = 3 ؛
int b = 3 ؛
المعالجة المترجم أولاً int a = 3 ؛ أولاً ، سيقوم بإنشاء مرجع في المكدس مع متغير A ، ثم معرفة ما إذا كانت هناك قيمة 3 في المكدس. إذا لم يتم العثور عليها ، فسيتم تخزينها 3 ثم توجه A إلى 3. ثم Process Int B = 3 ؛ بعد إنشاء المتغير المرجعي لـ B ، نظرًا لوجود بالفعل قيمة 3 في المكدس ، يشير B مباشرة إلى 3. وبهذه الطريقة ، يشير كل من كلا إلى 3 في نفس الوقت.
في هذا الوقت ، إذا تم تعيين = 4 مرة أخرى ؛ ثم سيبحث المترجم مرة أخرى عما إذا كانت هناك 4 قيمة في المكدس. إذا لم يكن كذلك ، تخزين 4 والنقطة من A إلى 4 ؛ إذا كان موجودًا بالفعل ، فأرّ إلى هذا العنوان مباشرة. لذلك ، لن يؤثر التغيير في القيمة A على القيمة ب.
تجدر الإشارة إلى أن مشاركة البيانات هذه تختلف عن مشاركة المراجع من كائنين يشيران إلى كائن واحد في نفس الوقت ، لأنه في هذه الحالة لن يؤثر تعديل A على B ، يتم ذلك بواسطة المترجم ، الذي يفضي إلى توفير المساحة. يعدل متغير مرجع الكائن الحالة الداخلية لهذا الكائن وسيؤثر على متغير مرجع كائن آخر.
كود جافا
1.int i1 = 9 ؛
2.int i2 = 9 ؛
3.int i3 = 9 ؛
4. Public Static Final int1 = 9 ؛
5. Public Static Final Int2 = 9 ؛
6.Public Static Final Int3 = 9 ؛
بالنسبة للمتغيرات الأعضاء والمتغيرات المحلية: متغيرات الأعضاء هي متغيرات محددة داخل الطريقة والفئة ؛ المتغيرات المحلية هي متغيرات محددة داخل الطريقة أو كتلة العبارة. يجب تهيئة المتغيرات المحلية.
المعلمات الرسمية هي متغيرات محلية ، وبيانات المتغيرات المحلية موجودة في ذاكرة المكدس. تختفي المتغيرات المحلية في ذاكرة المكدس عندما تختفي الطريقة.
يتم تخزين متغيرات الأعضاء في الكائنات في الكومة ويتم جمعها بواسطة جامع القمامة.
كما في الكود التالي:
كود جافا
فئة الميلاد. الشهر الخاص العام الخاص تاريخ الميلاد العام (int d ، int m ، int y) {day = d ؛ الشهر = م ؛ سنة = ص ؛ } حذف GET ، SET Method ............................................................................................................................................................................................................................................... اختبار اختبار = اختبار جديد () ؛ Test.Change (Date) ؛ تاريخ الميلاد D1 = عملية ميلاد جديدة (7،7،1970) ؛ } public void change1 (int i) {i = 1234 ؛ }بالنسبة للرمز أعلاه ، يعد Date متغيرًا محليًا ، و I و D و M و Y كلها معلمات رسمية كمتغيرات محلية ، واليوم والشهر والعام هي متغيرات الأعضاء. دعنا نحلل التغييرات أثناء تنفيذ الكود:
1. تبدأ الطريقة الرئيسية في التنفيذ: INT DATE = 9 ؛
تاريخ المتغيرات المحلية ، والأنواع الأساسية ، والمراجع والقيم كلها موجودة على المكدس.
2. اختبار الاختبار = اختبار جديد () ؛
الاختبار هو مرجع كائن ، وهو موجود على المكدس ، ويوجد الكائن (اختبار جديد ()) على الكومة.
3. Test.Change (Date) ؛
أنا متغير محلي ، والمرجع والقيمة موجودة في المكدس. عندما يتم تنفيذ تغيير الطريقة ، سأختفي من المكدس.
4. تاريخ الميلاد D1 = حالة ميلاد جديدة (7،7،1970) ؛
D1 هو مرجع كائن ويوجد في المكدس. توجد الكائنات (توقيع ميلاد جديد ()) في الكومة ، حيث تكون D و M و Y متغيرات محلية مخزنة في المكدس ، وأنواعها هي الأنواع الأساسية ، لذلك يتم تخزين بياناتها أيضًا في المكدس. اليوم ، الشهر ، السنة هي متغيرات الأعضاء ، ويتم تخزينها في كومة (تاريخ الميلاد الجديد ()). عندما يتم تنفيذ مُنشئ تاريخ الميلاد ، سوف يختفي D ، M ، Y من المكدس.
5. بعد تنفيذ الطريقة الرئيسية ، سيختفي متغير التاريخ ، واختبار ، ومرجع D1 من المكدس ، واختبار جديد () ، سوف ينتظر New Birthdate () جمع القمامة.
حمام سباحة ثابت
تشير التجمعات الثابتة إلى بعض البيانات التي يتم تحديدها خلال فترة التجميع ويتم حفظها في ملف .class المترجم.
بالإضافة إلى احتواء القيم الثابتة (النهائية) لمختلف الأنواع الأساسية (مثل int ، طويلة ، وما إلى ذلك) وأنواع الكائنات (مثل السلسلة والصفائف) المحددة في الكود ، فإنها تحتوي أيضًا على بعض المراجع الرمزية في شكل النص ، مثل:
◆ الأسماء المؤهلة بالكامل للفئات والواجهات ؛
◆ اسم ووصف الحقل ؛
◆ الطرق والأسماء والواصفات.
إذا تم إنشاء فترة الترجمة (محددة مباشرة في عروض أسعار مزدوجة) ، فسيتم تخزينها في التجمع الثابت ، وإذا كان يمكن تحديدها بحلول فترة التشغيل (من جديد) ، فسيتم تخزينها في الكومة. بالنسبة للسلاسل ذات المساواة ، لا يوجد دائمًا نسخة واحدة فقط في المجموعة الثابتة ونسخة متعددة في الكومة.
السلسلة هي بيانات تغليف خاصة. يمكن استخدامه:
كود جافا
String str = new String ("ABC") ؛ String str = "ABC" ؛هناك نوعان لإنشاء. الأول هو استخدام جديد () لإنشاء كائن جديد ، سيتم تخزينه في الكومة. يتم إنشاء كائن جديد في كل مرة يطلق عليها. النوع الثاني هو أولاً إنشاء STR متغير إلى كائن فئة السلسلة في المكدس ، ثم استخدم مرجعًا رمزيًا لمعرفة ما إذا كان هناك "ABC" في تجمع السلسلة الثابت. إذا لم يكن الأمر كذلك ، فقم بتخزين "ABC" في المسبح الثابت للسلسلة واترك Str يشير إلى "ABC". إذا كان هناك بالفعل "ABC" ، فدع Str يشير مباشرة إلى "ABC".
عند مقارنة ما إذا كانت القيم في الفصل متساوية ، استخدم طريقة متساوية () ؛ عند اختبار ما إذا كانت مراجع فئتي التفاف تشير إلى نفس الكائن ، استخدم == ، واستخدم المثال أدناه لتوضيح النظرية أعلاه.
كود جافا
string str1 = "abc" ؛ string str2 = "abc" ؛ system.out.println (str1 == str2) ؛ //حقيقي
يمكن ملاحظة أن Str1 و Str2 يشيران إلى نفس الكائن.
كود جافا
String str1 = سلسلة جديدة ("ABC") ؛ String str2 = سلسلة جديدة ("ABC") ؛ system.out.println (str1 == str2) ؛ // خطأ شنيعالطريقة الجديدة هي إنشاء كائنات مختلفة. توليد واحدة في وقت واحد.
لذلك ، بالطريقة الثانية ، يتم إنشاء سلاسل "ABC" المتعددة ، وهناك كائن واحد فقط في الذاكرة. طريقة الكتابة هذه مفيدة وتوفر مساحة الذاكرة. في الوقت نفسه ، يمكن أن يحسن سرعة تشغيل البرنامج إلى حد ما ، لأن JVM ستقرر تلقائيًا ما إذا كان من الضروري إنشاء كائن جديد بناءً على الموقف الفعلي للبيانات في المكدس. بالنسبة إلى رمز السلسلة STR = سلسلة جديدة ("ABC") ؛ ، يتم إنشاء كائنات جديدة في الكومة بغض النظر عما إذا كانت قيم السلسلة متساوية أم لا ، ما إذا كان من الضروري إنشاء كائنات جديدة ، وبالتالي زيادة العبء على البرنامج.
من ناحية أخرى ، ملاحظة: عندما نحدد فئة باستخدام تنسيق مثل String STR = "ABC" ؛ ، نأخذ دائمًا أمرا مفروغًا منه أننا ننشئ كائنًا من فئة السلسلة. تقلق بشأن الفخ! قد لا يكون الكائن قد تم إنشاؤه! وربما فقط أشير إلى كائن تم إنشاؤه سابقًا. فقط من خلال الطريقة الجديدة () يمكننا التأكد من إنشاء كائن جديد في كل مرة.
عدة أمثلة لمشكلة التجميع الثابتة للسلسلة
مثال 1:
كود جافا
السلسلة s0 = "kvill" ؛ string s1 = "kvill" ؛ string s2 = "kv" + "ill" ؛ system.out.println (s0 == s1) ؛ system.out.println (s0 == s2) ؛ والنتيجة هي: truetrue
التحليل: أولاً وقبل كل شيء ، نحتاج إلى معرفة أن النتيجة هي أن Java ستضمن أن يكون ثابت السلسلة نسخة واحدة فقط.
نظرًا لأن S0 و S1 في المثال هما ثوابت السلسلة ، يتم تحديدهما خلال فترة التجميع ، لذلك S0 == S1 صحيح ؛ و "KV" و "Ill" هي أيضا ثوابت سلسلة. عندما يتم توصيل السلسلة بواسطة ثوابت سلسلة متعددة ، فهي بالتأكيد ثابتة سلسلة ، لذلك يتم تحليل S2 أيضًا في سلسلة ثابتة خلال فترة الترجمة ، لذلك S2 هو أيضًا إشارة إلى "Kvill" في التجمع الثابت. لذلك نحصل على S0 == S1 == S2 ؛
مثال 2:
مثال:
كود جافا
التحليل: الأوتار التي تم إنشاؤها باستخدام سلسلة جديدة () ليست ثوابت ولا يمكن تحديدها خلال فترة التجميع ، وبالتالي فإن الأوتار التي أنشأتها سلسلة جديدة () لا يتم وضعها في البركة الثابتة ، ولها مساحة عنوان خاصة بها.
S0 هو أيضًا تطبيق لـ "Kvill" في تجمع ثابت. لا يمكن تحديد S1 خلال فترة التجميع ، لذلك فهو إشارة إلى الكائن الجديد "Kvill" الذي تم إنشاؤه في وقت التشغيل. لا يمكن تحديد S2 خلال فترة التجميع لأنه يحتوي على النصف الثاني من السلسلة الجديدة ("Ill") ، لذلك فهو أيضًا تطبيق للكائن الذي تم إنشاؤه حديثًا "Kvill" ؛ إذا فهمت هذه ، فستعرف لماذا يتم الحصول على هذه النتيجة.
مثال 3:
كود جافا
String a = "a1" ؛ string b = "a" + 1 ؛ system.out.println ((a == b)) ؛ // result = true string a = "true" ؛ string b = "a" + "true" ؛ system.out.println ((a == b)) ؛ // result = true string a = "a3.4" ؛ string b = "a" + 3.4 ؛ system.out.println ((a == b)) ؛ // النتيجة = صواب
التحليل: بالنسبة لاتصال JVM من ثوابت السلسلة ، يقوم JVM بتحسين اتصال "+" للسلسلة الثابتة بالقيمة المتصلة بعد فترة تجميع البرنامج. خذ "A" + 1 كمثال. بعد التحسين من قبل المترجم ، هو بالفعل A1 في الفصل. خلال فترة التجميع ، يتم تحديد قيمة ثابت السلسلة ، وبالتالي فإن النتيجة النهائية للبرنامج أعلاه صحيحة.
مثال 4:
كود جافا
String a = "ab" ؛ string bb = "b" ؛ string b = "a" + bb ؛ system.out.println ((a == b)) ؛ // النتيجة = خطأ
التحليل: بالنسبة لمراجع السلسلة في JVM ، نظرًا لوجود مراجع سلسلة في اتصال " +" للسلاسل ، لا يمكن تحديد القيمة المشار إليها أثناء فترة تجميع البرنامج ، أي "A + BB لا يمكن تحسينها بواسطة المترجم ، ويقوم فقط بتوزيع وتخصيص العنوان الجديد المتصاعد على B أثناء فترة تشغيل البرنامج. لذلك ، فإن نتيجة البرنامج أعلاه خاطئة.
مثال 5:
كود جافا
السلسلة A = "AB" ؛ السلسلة النهائية bb = "b" ؛ string b = "a" + bb ؛ system.out.println ((a == b)) ؛ // النتيجة = صواب
التحليل: الفرق الوحيد بين [4] هو أن سلسلة BB مزينة بالتعديل النهائي. بالنسبة للمتغيرات النهائية المعدلة ، يتم تحليلها كنسخة محلية من القيمة الثابتة في وقت الترجمة وتخزينها في حمام السباحة الثابت الخاص بها أو مضمن في تيار البود. لذلك في هذا الوقت آثار "A" + BB و "A" + "B" هي نفسها. لذلك ، فإن نتيجة البرنامج أعلاه صحيحة.
مثال 6:
كود جافا
السلسلة A = "AB" ؛ السلسلة النهائية bb = getBB () ؛ string b = "a" + bb ؛ system.out.println ((a == b)) ؛ // result = falseprivate static string getBb () {return "b" ؛ }التحليل: يشير JVM BB للسلاسل ، ولا يمكن تحديد قيمته خلال فترة التجميع. فقط بعد استدعاء الطريقة أثناء وقت تشغيل البرنامج ، يتم توصيل قيمة إرجاع الطريقة و "A" ديناميكيًا ويتم تعيين العنوان إلى B. لذلك ، فإن نتيجة البرنامج أعلاه خاطئة.
حول السلسلة غير قابلة للتغيير
من المثال أعلاه ، يمكننا معرفة:
String s = "A" + "B" + "C" ؛
يعادل سلسلة s = "ABC" ؛
سلسلة A = "A" ؛
السلسلة B = "B" ؛
سلسلة C = "C" ؛
سلسلة s = a + b + c ؛
هذا مختلف ، والنتيجة النهائية تساوي:
كود جافا
stringbuffer temp = new StringBuffer () ؛ temp.append (a) .Append (b) .Append (c) ؛ string s = temp.toString () ؛
من نتائج التحليل أعلاه ، ليس من الصعب استنتاج أن السلسلة تستخدم مشغل الاتصال (+) لتحليل سبب عدم الكفاءة ، مثل هذا الرمز:
كود جافا
اختبار الفئة العامة {public static void main (String args []) {String s = null ؛ لـ (int i = 0 ؛ i <100 ؛ i ++) {s+= "a" ؛ }}}في كل مرة يتم فيها + ، يتم إنشاء كائن StringBuilder ، ثم يلحقه ويرميه بعيدًا. في المرة التالية التي تصل فيها الحلقة ، يتم تجديد كائن StringBuilder ، ثم إلحاق السلسلة ، ويتم إكمال الحلقة حتى تنتهي. إذا استخدمنا مباشرة كائن StringBuilder لإلحاقه ، فيمكننا حفظ وقت N - 1 لإنشاء الكائن وتدميره. لذلك ، بالنسبة للتطبيقات التي تتطلب تسلسل السلسلة في حلقة ، يتم تنفيذ عملية الإلحاح بشكل عام باستخدام كائنات StringBuffer أو StringBulider.
بسبب الطبيعة غير القابلة للتغيير لفئة السلسلة ، هناك الكثير لتقوله حول هذا الموضوع. طالما أنك تعلم أن مثيل السلسلة لن يتغير بمجرد إنشاؤه ، على سبيل المثال: String str = "kv"+"ill"+""+"ans" ؛ هناك 4 ثوابت سلسلة ، أول "KV" و "Ill" تولد "Kvill" في الذاكرة ، ثم "Kvill" و "" توليد "kvill" و "" وأخيراً توليد "kvill ans" ؛ وتعيين عنوان هذه السلسلة إلى string ، لأن سلسلة "غير قابلة للتنفيذ" تولد العديد من المتغيرات المؤقتة ، وهذا هو السبب الذي يوصى به لاستخدام Stringbuffer.
الاستخدام النهائي والتفاهم في السلسلة
كود جافا
Final StringBuffer A = New StringBuffer ("111") ؛ Final StringBuffer B = New StringBuffer ("222") ؛ A = B ؛ // لا يتم تجميع هذه الجملة حتى يتم الانتهاء منها. StringBuffer النهائي A = New StringBuffer ("111") ؛ A.Append ("222") ؛ /// بعد الانتهاء منهيمكن ملاحظة أن النهائي صالح فقط لـ "القيمة" المشار إليها (أي عنوان الذاكرة). يجبر الإشارة إلى الإشارة فقط إلى الكائن الذي تم توجيهه في البداية. سيؤدي تغيير توجيهه إلى خطأ في وقت الترجمة. أما بالنسبة للتغييرات في الكائن الذي يشير إليه ، فإن النهائي غير مسؤول.
لخص
يتم استخدام المكدس لتخزين بعض البيانات المتغيرة المحلية لنوع البيانات الأصلي والمراجع إلى الكائنات (السلسلة ، الصفيف ، الكائن ، إلخ) ولكن لا يخزن محتوى الكائن
يتم تخزين الكائنات التي تم إنشاؤها باستخدام الكلمة الرئيسية الجديدة في الكومة.
السلسلة هي فئة غلاف خاصة ، ويتم تخزين مراجعها في المكدس ، ويجب تحديد محتوى الكائن وفقًا لطريقة الإنشاء (حمام سباحة ثابتة وكومة). يتم إنشاء بعضها في وقت الترجمة وتخزينه في مجموعة ثابتة السلسلة ، بينما يتم إنشاء البعض الآخر فقط في وقت التشغيل. استخدم الكلمة الرئيسية الجديدة وتخزينها في الكومة.
تتحدث المقالة أعلاه لفترة وجيزة عن الفرق بين تخصيص الذاكرة Java+ وموقع التخزين المتغير هو كل المحتوى الذي أشاركه معك. آمل أن تتمكن من إعطائك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.