خارج الذاكرة: من حيث النوع العادي ، فهذا يعني أنه لا توجد ذاكرة كافية. على سبيل المثال ، سيؤدي إنشاء كائن كبير باستمرار في حلقة لا حصر لها إلى تدفق الذاكرة.
تسرب الذاكرة: يشير إلى الإصدار في الوقت المناسب للذاكرة بعد تخصيص الذاكرة لكائن ما عندما لم يعد الكائن قيد الاستخدام ، مما يؤدي إلى احتلال وحدة الذاكرة وتقليل الذاكرة الفعلية المتاحة ، تمامًا مثل تسرب الذاكرة.
تسرب الذاكرة الناجم عن طريقة التسجيل
Substring (int bathindex ، int endndex) هي طريقة لفئة السلسلة ، ولكن يتم تنفيذ هذه الطريقة بشكل مختلف تمامًا في JDK6 و JDK7 (على الرغم من أنهما يحققان نفس التأثير). يمكن أن يساعدك فهم تفاصيل التنفيذ في استخدامها بشكل أفضل ، لأن الاستخدام غير السليم للفرعية في JDK1.6 يمكن أن يؤدي إلى مشاكل في تسرب الذاكرة الخطيرة.
1. دور السلسلة الفرعية
تقوم طريقة Sundring (int BeginIndex ، Int EndIndex) بإرجاع سلسلة فرعية ، بدءًا من بداية السلسلة الأصل وتنتهي في EndIndex-1. يبدأ تراكم السلسلة الأصل عند 0 ، ويحتوي السلسلة الفرعية على stalendex وليس endindex
string x = "abcdef" ؛ x = str.substring (1،3) ؛ system.out.println (x) ؛
إخراج البرنامج أعلاه هو "قبل الميلاد"
2. مبدأ التنفيذ
فئة السلسلة غير قابلة للتغيير. عندما يتم إعادة تعيين x في الجملة الثانية أعلاه ، سيشير إلى كائن سلسلة جديد. ومع ذلك ، لا يوجد وصف دقيق أو يمثل الموقف الفعلي الذي يحدث في الكومة. ما يحدث حقًا عندما يسمى السلسلة الفرعية هو الفرق بين الاثنين.
التنفيذ الأساسي في JDK6
يتم تخزين كائن السلسلة كصفيف char. هناك 3 حقول في فئة السلسلة: char [] القيمة ، إزاحة int ، وعدد int ، والتي يتم استخدامها على التوالي لتخزين صفيف الأحرف الحقيقي ، وموضع البداية للمصفوفة ، وعدد أحرف السلسلة. يمكن لهذه المتغيرات 3 تحديد سلسلة. عندما يتم استدعاء طريقة السلسلة الفرعية ، فإنها ستنشئ سلسلة جديدة ، لكن قيمة صفيف Char أعلاه ستظل تستخدم قيمة صفيف الأصل الأصلي. الفرق الوحيد بين الصفيف الأصل والطفل هو أن قيم العد والإزاحة مختلفة.
ألقِ نظرة على رمز المصدر لتنفيذ السلسلة الفرعية في JDK6:
السلسلة العامة Substring (int bathindex ، int endIndex) {if (begrenindex <0) {رمي new StringIndExOutofBoundSexception (stablyendex) ؛ } if (endIndex> count) {رمي new StringIndExOutOfBoundSexception (endIndex) ؛ } if (begrenindex> endIndex) {رمي new StringIndExOutOfBoundSexception (endIndex - stablindex) ؛ } return ((Begrenindex == 0) && (endIndex == count))؟ هذا: سلسلة جديدة (Offset + BeginIndex ، EndIndex - Beathindex ، value) ؛ // يتم استخدامه لاستخدام نفس قيمة صفيف char مثل السلسلة الأصل} string (int offset ، int count ، char value []) {this.value = value ؛ this.offset = الإزاحة ؛ this.count = count ؛ }String str = "abcdefghijklmnopqrst" ؛ السلسلة الفرعية = str.substring (1 ، 3) ؛ str = null ؛
يحتوي هذا البرنامج البسيط على متغيرين سلسلة Str و Sub. يتم الحصول على السلسلة الفرعية بواسطة سلسلة الأصل str. إذا تم تشغيل البرنامج أعلاه في JDK1.6 ، فإننا نعلم أن تخصيص مساحة الذاكرة للمصفوفة يتم تنفيذها على الكومة ، فإن قيمة صفيف char الداخلية من Sub و Str هي نفسها ، أي صفيف Char أعلاه المكون من أحرف a ~ حرف t. الفرق الوحيد بين STR و SUB هو الفرق بين chinindex وعدد طول الأحرف في الصفيف. في الجملة الثالثة ، نجعل مرجع STR فارغًا ، والذي يهدف إلى تحرير المساحة التي تشغلها STR ، ولكن في هذا الوقت ، لا يمكن لـ GC إعادة تدوير صفيف Char هذا لأنه لا يزال يتم الرجوع إليه داخل السلسلة الفرعية ، على الرغم من أن Sub يعترض فقط جزءًا صغيرًا من هذه المجموعة الكبيرة. عندما تكون STR سلسلة كبيرة جدًا ، تكون هذه النفايات واضحة للغاية وقد تؤدي إلى مشاكل في الأداء. يمكنك حل هذه المشكلة بواسطة:
يستخدم تقنية الربط السلسلة ، والتي ستنشئ سلسلة جديدة. ستستخدم هذه السلسلة الجديدة مجموعة char داخلية جديدة لتخزين الشخصيات التي يحتاجها بالفعل ، بحيث لن تتم الإشارة إلى مجموعة char من مجموعة الأصل من قبل مصادر أخرى. دع str = null ، وسيتم إعادة تدوير المساحة بأكملها من قبل STR في المرة التالية التي يتم فيها إعادة تدوير GC. لكن من الواضح أن الكتابة مثل هذا ليست جيدة المظهر ، لذلك في JDK7 ، يتم إعادة تنفيذ السلسلة الفرعية.
التنفيذ الفرعي في JDK7
تحسين تنفيذ السلسلة الفرعية في JDK7 ، والتي تنشئ بالفعل صفيف char جديد في الكومة للفرعية المعتادة للاحتفاظ بأحرف للفروس.
تحقق من رمز مصدر التنفيذ لطريقة السلسلة الفرعية لفئة السلسلة في JDK7:
السلسلة العامة Substring (int bathindex ، int endIndex) {if (begrenindex <0) {رمي new StringIndExOutofBoundSexception (stablyendex) ؛ } if (endIndex> value.length) {رمي new StringIndExOutOfBoundSexception (EndIndex) ؛ } int sublen = endIndex - beginindex ؛ if (sublen <0) {رمي new StringIndExOutOfBoundSexception (sublen) ؛ } الإرجاع ((stalingindex == 0) && (endIndex == value.length))؟ هذا: سلسلة جديدة (القيمة ، BeginIndex ، Sublen) ؛ } السلسلة العامة (قيمة char [] ، int الإزاحة ، int count) {if (إزاحة <0) {رمي new StringIndExOutofBoundSexception (الإزاحة) ؛ } if (count <0) {refl new StringIndExOutofBoundSexception (count) ؛ } // ملاحظة: قد يكون الإزاحة أو العد بالقرب من -1 >>> 1. if (Offset> value.length - count) {رمي new StringIndExOutOfBoundSexception (الإزاحة + العد) ؛ } هذا. }طريقة copyofrange لفئة المصفوفات:
char char static static [] copyOfrange (char [] أصلي ، int من ، int إلى) {int newLength = to - from ؛ إذا (NewLength <0) رمي جديد غير unalfalArgumentException (من + ">" + إلى) ؛ char [] copy = new Char [newLength] ؛ // هو إنشاء نظام صفيف char جديد. نسخة إرجاع }يمكن العثور على صفيف char جديد للتقدم لتخزين الشخصيات في السلسلة الفرعية. وبهذه الطريقة ، لا يوجد اتصال ضروري بين سلسلة الأطفال والسلسلة الأصل. عندما تكون مرجع السلسلة الأصل غير صالحة ، ستقوم GC بإعادة تدوير مساحة الذاكرة التي تحتلها السلسلة الأصل في الوقت المناسب.
لخص
ما سبق هو التفسير الكامل لتسرب الذاكرة الناجم عن طريقة التسجيل في Java. آمل أن يكون ذلك مفيدًا للجميع. يمكن للأصدقاء المهتمين الاستمرار في الرجوع إلى الموضوعات الأخرى ذات الصلة على هذا الموقع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع!