นอกหน่วยความจำ: ในแง่ของคนธรรมดาหมายความว่ามีหน่วยความจำไม่เพียงพอ ตัวอย่างเช่นการสร้างวัตถุขนาดใหญ่อย่างต่อเนื่องในลูปที่ไม่มีที่สิ้นสุดในไม่ช้าจะทำให้หน่วยความจำล้น
การรั่วไหลของหน่วยความจำ: หมายถึงการปล่อยหน่วยความจำในเวลาที่เหมาะสมหลังจากจัดสรรหน่วยความจำให้กับวัตถุเมื่อวัตถุไม่ได้ใช้งานอีกต่อไปส่งผลให้หน่วยหน่วยความจำถูกครอบครองและหน่วยความจำที่มีอยู่จริงจะลดลงเช่นเดียวกับการรั่วไหลของหน่วยความจำ
การรั่วไหลของหน่วยความจำที่เกิดจากวิธีการย่อย
Substring (Int Beginindex, Int EndnDex) เป็นวิธีการของคลาสสตริง แต่วิธีนี้ถูกนำมาใช้แตกต่างกันอย่างสิ้นเชิงใน JDK6 และ JDK7 (แม้ว่าพวกเขาทั้งคู่จะได้รับผลเดียวกัน) การทำความเข้าใจรายละเอียดการใช้งานของพวกเขาสามารถช่วยให้คุณใช้งานได้ดีขึ้นเนื่องจากการใช้งานย่อยที่ไม่เหมาะสมใน JDK1.6 สามารถนำไปสู่ปัญหาการรั่วไหลของหน่วยความจำที่ร้ายแรง
1. บทบาทของ substring
เมธอดย่อย (int beartIndex, int endindex) ส่งคืนสตริงย่อยเริ่มต้นจาก entalingindex ของสตริงพาเรนต์และสิ้นสุดที่ endindex-1 ตัวห้อยของสตริงพาเรนต์เริ่มต้นที่ 0 และสตริงย่อยมี entalingindex และไม่ใช่ endindex
สตริง x = "abcdef"; x = str.substring (1,3); system.out.println (x);
ผลลัพธ์ของโปรแกรมข้างต้นคือ "BC"
2. หลักการดำเนินการ
คลาสสตริงไม่เปลี่ยนรูป เมื่อ X ถูกกำหนดใหม่ในประโยคที่สองด้านบนมันจะชี้ไปที่วัตถุสตริงใหม่ อย่างไรก็ตามไม่มีคำอธิบายที่ถูกต้องหรือแสดงถึงสถานการณ์จริงที่เกิดขึ้นในกอง สิ่งที่เกิดขึ้นจริง ๆ เมื่อเรียกว่า substring คือความแตกต่างระหว่างทั้งสอง
การใช้งาน Substring ใน JDK6
วัตถุสตริงถูกเก็บไว้เป็นอาร์เรย์ถ่าน มี 3 ฟิลด์ในคลาสสตริง: ค่า char [], การชดเชย int และจำนวน int ซึ่งใช้ตามลำดับเพื่อจัดเก็บอาร์เรย์อักขระจริงตำแหน่งเริ่มต้นของอาร์เรย์และจำนวนอักขระของสตริง ตัวแปรทั้ง 3 นี้สามารถกำหนดสตริงได้ เมื่อเรียกใช้วิธีการย่อยมันจะสร้างสตริงใหม่ แต่ค่าอาร์เรย์ถ่านข้างต้นจะยังคงใช้ค่าของอาร์เรย์หลักดั้งเดิม ความแตกต่างเพียงอย่างเดียวระหว่างผู้ปกครองและอาร์เรย์เด็กคือค่าของการนับและออฟเซ็ตนั้นแตกต่างกัน
ดูที่ซอร์สโค้ดของการใช้งาน Substring ใน JDK6:
สตริงย่อยสาธารณะ (int beginindex, int endindex) {ถ้า (startIndex <0) {โยน stringIndExOutOfBoundSexception ใหม่ (engartIndex); } if (endindex> count) {โยน stringIndExOutOfBoundSexception ใหม่ (endindex); } if (beginindex> endIndex) {โยน stringIndExouToFBoundSexception ใหม่ (EndIndex - bENGININDEX); } return ((enginindex == 0) && (endindex == นับ))? สิ่งนี้: สตริงใหม่ (ออฟเซ็ต + entalindex, endindex - startIndex, ค่า); // มันถูกใช้เพื่อใช้ค่าอาร์เรย์ถ่านเดียวกันกับสตริงหลัก} String (int Offset, int count, ค่าถ่าน []) {this.value = value; this.offset = ชดเชย; this.count = นับ; -string str = "abcdefghijklmnopqrst"; string sub = str.substring (1, 3); str = null;
โปรแกรมง่าย ๆ นี้มีตัวแปรสตริงสองตัว STR และ SUB สตริงย่อยได้มาจากสตริงหลัก Str หากโปรแกรมข้างต้นทำงานใน JDK1.6 เรารู้ว่าการจัดสรรพื้นที่หน่วยความจำของอาร์เรย์จะดำเนินการบนกองดังนั้นค่าอาร์เรย์ถ่านภายในของย่อยและ STR นั้นเหมือนกันนั่นคืออาร์เรย์ถ่านข้างต้นประกอบด้วยอักขระ ~ ตัวละคร t ความแตกต่างเพียงอย่างเดียวระหว่าง STR และ SUB คือความแตกต่างระหว่างการนับความยาวเริ่มต้นและการนับความยาวอักขระในอาร์เรย์ ในประโยคที่สามเราทำการอ้างอิง STR ที่ว่างเปล่าซึ่งมีจุดประสงค์เพื่อปลดปล่อยพื้นที่ที่ถูกครอบครองโดย STR แต่ในเวลานี้ GC ไม่สามารถรีไซเคิลอาร์เรย์ถ่านขนาดใหญ่นี้ได้เนื่องจากยังคงถูกอ้างอิงภายในสตริงย่อย เมื่อ STR เป็นสายที่มีขนาดใหญ่มากขยะนี้ชัดเจนมากและอาจทำให้เกิดปัญหาด้านประสิทธิภาพ คุณสามารถแก้ปัญหานี้ได้โดย:
มันใช้เทคโนโลยีการประกบสตริงซึ่งจะสร้างสตริงใหม่ สตริงใหม่นี้จะใช้อาร์เรย์ถ่านภายในใหม่เพื่อจัดเก็บอักขระที่ต้องการจริง ๆ ดังนั้นอาร์เรย์ถ่านของอาร์เรย์หลักจะไม่ถูกอ้างอิงจากแหล่งข้อมูลอื่น ให้ str = null และพื้นที่ทั้งหมดที่ถูกครอบครองโดย STR จะถูกรีไซเคิลในครั้งต่อไป GC จะถูกนำกลับมาใช้ใหม่ แต่การเขียนแบบนี้ไม่ได้ดูดีดังนั้นใน JDK7 Substring จะถูกนำมาใช้ใหม่อีกครั้ง
การใช้งาน Substring ใน JDK7
ปรับปรุงการใช้งานของ substring ใน JDK7 ซึ่งจริง ๆ แล้วสร้างอาร์เรย์ถ่านใหม่ในกองเพื่อสำหรับ substrings ที่สกัดกั้นเพื่อเก็บอักขระสำหรับ substrings
ตรวจสอบซอร์สโค้ดการใช้งานของวิธีการย่อยของคลาสสตริงใน JDK7:
สตริงย่อยสาธารณะ (int beginindex, int endindex) {ถ้า (startIndex <0) {โยน stringIndExOutOfBoundSexception ใหม่ (engartIndex); } if (endindex> value.length) {โยน stringIndExOutOfBoundSexception ใหม่ (endIndex); } int subblen = endindex - entarmindex; if (sublen <0) {โยน stringIndExOutOfBoundSexception ใหม่ (SUBLEN); } return ((enginindex == 0) && (endindex == value.length))? สิ่งนี้: สตริงใหม่ (ค่า, entalingindex, sublen); - สตริงสาธารณะ (ค่าถ่าน [], ออฟเซ็ต int, จำนวน int) {ถ้า (ออฟเซ็ต <0) {โยน stringIndExOutOfBoundSexception ใหม่ (ออฟเซ็ต); } if (count <0) {โยน stringIndExOutOfBoundSexception ใหม่ (นับ); } // หมายเหตุ: ชดเชยหรือนับอาจอยู่ใกล้ -1 >>> 1 if (Offset> value.length - Count) {โยน stringIndExOutOfBoundSexception ใหม่ (Offset + Count); } this.value = arrays.copyofRange (ค่า, ชดเชย, ออฟเซ็ต+นับ); -วิธี copyofrange ของคลาสอาร์เรย์:
ถ่านสาธารณะคงที่ [] copyofRange (char [] ต้นฉบับ, int จาก, int ถึง) {int newLength = to - จาก; ถ้า (newLength <0) โยน unlegalargumentException ใหม่ (จาก + ">" + ถึง); char [] copy = new Char [newLength]; // มันคือการสร้าง char array system.arraycopy (ต้นฉบับ, จาก, Copy, 0, Math.min (Original.length - จาก, newLength)); ส่งคืนสำเนา; -จะพบได้ว่าอาร์เรย์ถ่านใหม่ถูกสร้างขึ้นสำหรับ substring เพื่อจัดเก็บอักขระในสายย่อย ด้วยวิธีนี้ไม่มีการเชื่อมต่อที่จำเป็นระหว่างสตริงลูกและสตริงหลัก เมื่อการอ้างอิงของสตริงพาเรนต์ไม่ถูกต้อง GC จะรีไซเคิลพื้นที่หน่วยความจำที่ถูกครอบครองโดยสตริงหลัก
สรุป
ข้างต้นเป็นคำอธิบายทั้งหมดของการรั่วไหลของหน่วยความจำที่เกิดจากวิธีการย่อยใน Java ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!