การสร้างวัตถุสตริงใน Java มีสองรูปแบบ รูปแบบหนึ่งคือรูปแบบตัวอักษร เช่น String str = "droid"; และอีกรูปแบบหนึ่งคือการใช้ new ซึ่งเป็นวิธีมาตรฐานในการสร้างวัตถุ เช่น String str = new String( " droid"); ทั้งสองวิธีนี้มักใช้ในการเขียนโค้ด โดยเฉพาะวิธีการตามตัวอักษร อย่างไรก็ตาม จริงๆ แล้วมีความแตกต่างบางประการในด้านประสิทธิภาพและการใช้หน่วยความจำระหว่างการใช้งานทั้งสองนี้ ทั้งหมดนี้เกิดจากความจริงที่ว่าเพื่อลดการสร้างอ็อบเจ็กต์สตริงซ้ำ JVM จะรักษาหน่วยความจำพิเศษไว้
หลักการทำงาน
เมื่อวัตถุสตริงถูกสร้างขึ้นในรูปแบบของตัวอักษรในโค้ด JVM จะตรวจสอบตัวอักษรก่อน หากมีการอ้างอิงไปยังวัตถุสตริงที่มีเนื้อหาเดียวกันในพูลค่าคงที่สตริง การอ้างอิงจะถูกส่งกลับ มิฉะนั้น สตริงใหม่จะถูกสร้างขึ้น อ็อบเจ็กต์จะถูกสร้างขึ้น การอ้างอิงนี้จะถูกใส่ลงในพูลค่าคงที่ของสตริง และการอ้างอิงจะถูกส่งกลับ
ยกตัวอย่าง
แบบฟอร์มการสร้างตัวอักษร
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง str1 = "หุ่นยนต์";
JVM ตรวจพบตัวอักษรนี้ ในที่นี้เราคิดว่าไม่มีวัตถุที่มีเนื้อหาเป็น droid JVM ไม่พบการมีอยู่ของวัตถุสตริงที่มีเนื้อหาของ droid ผ่านพูลค่าคงที่สตริง จากนั้นจะสร้างวัตถุสตริง จากนั้นใส่การอ้างอิงของวัตถุที่สร้างขึ้นใหม่ลงในพูลค่าคงที่สตริง และส่งคืนการอ้างอิงไปที่ ตัวแปร str1
หากมีโค้ดแบบนี้ต่อไป
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง str2 = "หุ่นยนต์";
ในทำนองเดียวกัน JVM ยังคงต้องตรวจจับตัวอักษรนี้ JVM ค้นหาพูลค่าคงที่สตริงและพบว่ามีออบเจ็กต์สตริงที่มีเนื้อหาของ "droid" อยู่ ดังนั้นจึงส่งคืนการอ้างอิงของออบเจ็กต์สตริงที่มีอยู่ไปยังตัวแปร str2 โปรดทราบว่าออบเจ็กต์สตริงใหม่ไม่ได้ถูกสร้างขึ้นใหม่ที่นี่
เพื่อตรวจสอบว่า str1 และ str2 ชี้ไปที่วัตถุเดียวกันหรือไม่ เราสามารถใช้โค้ดนี้
คัดลอกรหัสรหัสดังต่อไปนี้:
System.out.println(str1 == str2);
ผลลัพธ์ก็เป็นจริง
สร้างโดยใช้ใหม่
คัดลอกรหัสรหัสดังต่อไปนี้:
String str3 = สตริงใหม่ ("droid");
เมื่อเราใช้ new เพื่อสร้างออบเจ็กต์สตริง ออบเจ็กต์สตริงใหม่จะถูกสร้างขึ้นโดยไม่คำนึงว่าจะมีการอ้างอิงไปยังออบเจ็กต์ที่มีเนื้อหาเดียวกันในพูลค่าคงที่สตริงหรือไม่ ดังนั้นเราจึงใช้โค้ดต่อไปนี้เพื่อทดสอบ
คัดลอกรหัสรหัสดังต่อไปนี้:
String str3 = สตริงใหม่ ("droid");
System.out.println(str1 == str3);
ผลลัพธ์ที่ได้จะเป็นเท็จอย่างที่เราคิด ซึ่งบ่งชี้ว่าตัวแปรทั้งสองชี้ไปที่วัตถุที่แตกต่างกัน
ฝึกงาน
สำหรับวัตถุสตริงที่สร้างขึ้นโดยใช้ใหม่ด้านบน หากคุณต้องการเพิ่มการอ้างอิงของวัตถุนี้ลงในพูลค่าคงที่สตริง คุณสามารถใช้วิธีฝึกงาน
หลังจากเรียกนักศึกษาฝึกงานแล้ว ให้ตรวจสอบก่อนว่ามีการอ้างอิงถึงออบเจ็กต์ในพูลค่าคงที่สตริงหรือไม่ หากมี ให้ส่งคืนการอ้างอิงไปยังตัวแปร
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง str4 = str3.intern();
System.out.println(str4 == str1);
ผลลัพธ์ที่ได้จะเป็นจริง
คำถามที่ยาก
วิชาบังคับก่อน?
ข้อกำหนดเบื้องต้นสำหรับการนำพูลค่าคงที่สตริงไปใช้คืออ็อบเจ็กต์ String ใน Java นั้นไม่เปลี่ยนรูป ซึ่งสามารถรับประกันได้ว่าตัวแปรหลายตัวจะใช้อ็อบเจ็กต์เดียวกันร่วมกัน หากวัตถุ String ใน Java ไม่แน่นอนและการดำเนินการอ้างอิงเปลี่ยนค่าของวัตถุ ตัวแปรอื่น ๆ ก็จะได้รับผลกระทบเช่นกัน เห็นได้ชัดว่านี่ไม่สมเหตุสมผล
การอ้างอิงหรือวัตถุ
ปัญหาที่พบบ่อยที่สุดคือว่าการอ้างอิงหรืออ็อบเจ็กต์ถูกเก็บไว้ในพูลค่าคงที่สตริงหรือไม่ พูลค่าคงที่สตริงเก็บการอ้างอิงวัตถุ ไม่ใช่วัตถุ ใน Java วัตถุจะถูกสร้างขึ้นในหน่วยความจำฮีป
อัปเดตการตรวจสอบ ความคิดเห็นมากมายที่ได้รับก็พูดถึงปัญหานี้เช่นกัน ฉันเพียงแค่ตรวจสอบเท่านั้น สภาพแวดล้อมการตรวจสอบ:
คัดลอกรหัสรหัสดังต่อไปนี้:
22:18:54-androidyue~/Videos$ cat /etc/os-release
NAME=หมวกฟาง
VERSION="17 (ปาฏิหาริย์เนื้อ)"
ID=หมวกฟาง
VERSION_ID=17
PRETTY_NAME="Fedora 17 (ปาฏิหาริย์เนื้อแน่น)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:17"
22:19:04-androidyue~/Videos$ java -version
จาวาเวอร์ชัน "1.7.0_25"
สภาพแวดล้อมรันไทม์ OpenJDK (fedora-2.3.12.1.fc17-x86_64)
OpenJDK 64-Bit Server VM (บิลด์ 23.7-b01, โหมดผสม)
แนวคิดในการตรวจสอบ: โปรแกรม Java ต่อไปนี้อ่านไฟล์วิดีโอที่มีขนาด 82M และดำเนินการฝึกงานในรูปแบบของสตริง
คัดลอกรหัสรหัสดังต่อไปนี้:
22:01:17-androidyue~/Videos$ ll -lh |. grep why_to_learn.mp4
-rw-rw-r-- 1 androidyue androidyue 82M 20 ต.ค. 2556 why_to_learn.mp4
รหัสยืนยัน
คัดลอกรหัสรหัสดังต่อไปนี้:
นำเข้า java.io.BufferedReader;
นำเข้า java.io.FileNotFoundException;
นำเข้า java.io.FileReader;
นำเข้า java.io.IOException;
TestMain คลาสสาธารณะ {
เนื้อหาไฟล์สตริงส่วนตัวแบบคงที่;
โมฆะคงที่สาธารณะ main (String [] args) {
fileContent = readFileToString (args [0]);
ถ้า (null != fileContent) {
fileContent = fileContent.intern();
System.out.println("ไม่เป็นโมฆะ");
-
-
สตริงคงที่ส่วนตัว readFileToString (ไฟล์ String) {
เครื่องอ่าน BufferedReader = null;
พยายาม {
reader = new BufferedReader (FileReader ใหม่ (ไฟล์));
บัฟ StringBuffer = StringBuffer ใหม่ ();
เส้นสาย;
ในขณะที่ ((line = reader.readLine()) != null) {
buff.ผนวก(บรรทัด);
-
กลับ buff.toString();
} จับ (FileNotFoundException จ) {
e.printStackTrace();
} จับ (IOException จ) {
e.printStackTrace();
} ในที่สุด {
ถ้า (null != ผู้อ่าน) {
พยายาม {
reader.ปิด();
} จับ (IOException จ) {
e.printStackTrace();
-
-
-
กลับเป็นโมฆะ;
-
-
เนื่องจากพูลค่าคงที่สตริงมีอยู่ในรุ่นถาวรในหน่วยความจำฮีป จึงสามารถใช้ได้ก่อน Java8 เราตรวจสอบสิ่งนี้โดยการตั้งค่าการสร้างถาวรให้เป็นค่าที่น้อยมาก หากมีอ็อบเจ็กต์สตริงอยู่ในพูลค่าคงที่ของสตริง ข้อผิดพลาดพื้นที่ permgen java.lang.OutOfMemoryError จะถูกโยนทิ้งอย่างหลีกเลี่ยงไม่ได้
คัดลอกรหัสรหัสดังต่อไปนี้:
java -XX:PermSize=6m TestMain ~/Videos/why_to_learn.mp4
การรันโปรแกรมพิสูจน์อักษรไม่ได้ทำให้เกิด OOM อันที่จริง ไม่สามารถพิสูจน์ได้เป็นอย่างดีว่าวัตถุหรือการอ้างอิงถูกจัดเก็บไว้หรือไม่
แต่อย่างน้อยนี่ก็พิสูจน์ได้ว่า char[] วัตถุเนื้อหาจริงของสตริงไม่ได้ถูกเก็บไว้ในพูลค่าคงที่ของสตริง ในกรณีนี้ จริงๆ แล้วไม่สำคัญว่าพูลค่าคงที่สตริงจะเก็บวัตถุสตริงหรือการอ้างอิงไปยังวัตถุสตริงหรือไม่ แต่โดยส่วนตัวแล้วฉันยังชอบเก็บไว้เป็นข้อมูลอ้างอิงมากกว่า
ข้อดีและข้อเสีย
ข้อดีของพูลค่าคงที่สตริงคือลดการสร้างสตริงที่มีเนื้อหาเดียวกันและประหยัดพื้นที่หน่วยความจำ
หากเรายืนกรานที่จะพูดถึงข้อเสีย ก็แสดงว่าเวลาในการประมวลผลของ CPU นั้นถูกสละเพื่อแลกกับพื้นที่ เวลาในการคำนวณ CPU ส่วนใหญ่จะใช้ในการค้นหาว่ามีการอ้างอิงไปยังอ็อบเจ็กต์ที่มีเนื้อหาเดียวกันในพูลค่าคงที่สตริงหรือไม่ อย่างไรก็ตาม การใช้งานภายในคือ HashTable ดังนั้นต้นทุนการคำนวณจึงต่ำ
GC รีไซเคิล?
เนื่องจากพูลค่าคงที่สตริงเก็บการอ้างอิงไปยังวัตถุสตริงที่ใช้ร่วมกัน นี่หมายความว่าวัตถุเหล่านี้ไม่สามารถรีไซเคิลได้หรือไม่
ประการแรก วัตถุที่ใช้ร่วมกันในคำถามโดยทั่วไปมีขนาดค่อนข้างเล็ก เท่าที่ฉันรู้ ปัญหานี้เกิดขึ้นในเวอร์ชันก่อนหน้า แต่ด้วยการแนะนำการอ้างอิงที่อ่อนแอ ปัญหานี้น่าจะหมดไปในตอนนี้
เกี่ยวกับปัญหานี้ คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับบทความนี้ที่ฝึกงาน Strings: Java Glossary
การใช้งานฝึกงาน?
ข้อกำหนดเบื้องต้นสำหรับการใช้นักศึกษาฝึกงานคือคุณต้องรู้ว่าจำเป็นต้องใช้มันจริงๆ ตัวอย่างเช่น เรามีบันทึกหลายล้านรายการที่นี่ และค่าหนึ่งในบันทึกคือแคลิฟอร์เนีย สหรัฐอเมริกา หลายครั้ง เราไม่ต้องการสร้างออบเจ็กต์สตริงดังกล่าวหลายล้านรายการ เราสามารถใช้ intern เพื่อเก็บสำเนาเดียวไว้ในหน่วยความจำได้ สามารถ. หากต้องการความเข้าใจเชิงลึกเกี่ยวกับการฝึกงาน โปรดดูที่การวิเคราะห์เชิงลึกของ String#intern
มีข้อยกเว้นอยู่เสมอหรือไม่?
คุณรู้หรือไม่ว่าโค้ดต่อไปนี้จะสร้างออบเจ็กต์สตริงหลายรายการและบันทึกการอ้างอิงหลายรายการในพูลค่าคงที่สตริง
คัดลอกรหัสรหัสดังต่อไปนี้:
การทดสอบสตริง = "a" + "b" + "c";
คำตอบก็คือ มีการสร้างออบเจ็กต์เดียวเท่านั้น และมีเพียงการอ้างอิงเดียวเท่านั้นที่ถูกบันทึกไว้ในพูลคงที่ เราสามารถค้นหาได้โดยใช้ javap เพื่อถอดรหัสและดู
คัดลอกรหัสรหัสดังต่อไปนี้:
17:02 $ javap -c TestInternedPoolGC
เรียบเรียงจาก "TestInternedPoolGC.java"
TestInternedPoolGC คลาสสาธารณะขยาย java.lang.Object {
TestInternedPoolGC สาธารณะ ();
รหัส:
0: aload_0
1: เรียกใช้พิเศษ #1; //Method java/lang/Object."<init>":()V
4: กลับ
โมฆะคงสาธารณะ main(java.lang.String[]) พ่น java.lang.Exception;
รหัส:
0: ldc #2; //สตริง abc
2: astore_1
3: กลับ
คุณเห็นไหมว่าระหว่างการรวบรวม ตัวอักษรทั้งสามนี้ถูกรวมเข้าด้วยกันเป็นหนึ่งเดียว? นี่คือการปรับให้เหมาะสมจริง ๆ เพื่อหลีกเลี่ยงการสร้างออบเจ็กต์สตริงที่ซ้ำซ้อน และไม่ทำให้เกิดปัญหาการประกบสตริง เกี่ยวกับการประกบสตริง คุณสามารถดูรายละเอียด Java ได้: การประกบสตริง