ในJavaString
วัตถุขนาดใหญ่สุดที่อาจมีหมายถึงlength()
การเรียกวิธีการคืออะไร?
ฉันรู้ว่าlength()
คืนขนาดของString
a char []
;
ในJavaString
วัตถุขนาดใหญ่สุดที่อาจมีหมายถึงlength()
การเรียกวิธีการคืออะไร?
ฉันรู้ว่าlength()
คืนขนาดของString
a char []
;
คำตอบ:
เมื่อพิจารณาถึงString
คลาสของlength
เมธอดจะส่งกลับint
ค่าความยาวสูงสุดที่จะถูกส่งคืนโดยเมธอดนั้นInteger.MAX_VALUE
คือ2^31 - 1
(หรือประมาณ 2 พันล้าน)
ในแง่ของความยาวและการจัดทำดัชนีของอาร์เรย์ (เช่นchar[]
ซึ่งอาจเป็นวิธีการใช้การแทนข้อมูลภายในสำหรับString
s) บทที่ 10: อาร์เรย์ของข้อกำหนดภาษา Java, Java SE 7 Editionกล่าวว่า:
ตัวแปรที่อยู่ในอาร์เรย์ไม่มีชื่อ แต่จะถูกอ้างอิงโดยนิพจน์การเข้าถึงอาร์เรย์ที่ใช้ค่าดัชนีจำนวนเต็มแบบไม่ลบ ตัวแปรเหล่านี้เรียกว่า ส่วนประกอบของอาเรย์ ถ้าอาเรย์มี
n
ส่วนประกอบเราจะบอกว่าn
เป็น ความยาวของอาเรย์ ส่วนประกอบของอาร์เรย์อ้างอิงโดยใช้ดัชนีจำนวนเต็มตั้งแต่0
ถึงn - 1
รวม
นอกจากนี้การจัดทำดัชนีจะต้องเป็นไปตามint
ค่าที่กล่าวไว้ในมาตรา 10.4 :
อาร์เรย์จะต้องจัดทำดัชนีโดย
int
ค่า
ดังนั้นจึงปรากฏว่าข้อ จำกัด นั้นแน่นอน2^31 - 1
เนื่องจากเป็นค่าสูงสุดสำหรับค่าที่ไม่ใช่int
ค่าลบ
อย่างไรก็ตามอาจมีข้อ จำกัด อื่น ๆ เช่นขนาดที่สามารถจัดสรรได้สูงสุดสำหรับอาร์เรย์
javac
ให้เกิดข้อผิดพลาดเกี่ยวกับที่เป็นตัวหนังสือยาวเกินไป:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
javac
สำหรับString
ตัวอักษร (ไม่ใช่String
วัตถุ) ที่ผมไม่สามารถหาอ้างอิงถึงขีด จำกัด ของขนาดString
ตัวอักษรในข้อมูลจำเพาะภาษา Java และ JVM ข้อมูลจำเพาะ ฉันพยายามสร้างString
ตัวอักษรที่มีขนาดใหญ่กว่า 100,000 ตัวอักษรและคอมไพเลอร์ Eclipse ไม่มีปัญหาในการรวบรวม (และการเรียกใช้โปรแกรมก็สามารถที่จะแสดงให้เห็นว่าตัวอักษรมีString.length
ขนาดใหญ่กว่า 100,000.)
java.io.DataInput.readUTF()
และjava.io.DataOutput.writeUTF(String)
บอกว่าString
วัตถุนั้นถูกแทนด้วยข้อมูลความยาวสองไบต์และการแทนUTF-8 ที่ดัดแปลงของตัวละครทุกตัวในสตริง นี้สรุปว่าความยาวของเชือกถูก จำกัด ด้วยจำนวนไบต์ของ UTF-8 เป็นตัวแทนการปรับเปลี่ยนของสตริงเมื่อใช้กับและDataInput
DataOutput
นอกจากนี้ข้อมูลจำเพาะที่CONSTANT_Utf8_info
พบในข้อมูลจำเพาะเครื่องเสมือน Java จะกำหนดโครงสร้างดังต่อไปนี้
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
คุณจะพบว่าขนาดของ 'ความยาว' เป็นสองไบต์
ว่าประเภทการกลับมาของวิธีการบางอย่าง (เช่นString.length()
) คือไม่ได้หมายความว่าค่าสูงสุดที่อนุญาตคือint
Integer.MAX_VALUE
ในกรณีส่วนใหญ่int
จะเลือกด้วยเหตุผลด้านประสิทธิภาพเท่านั้น ข้อกำหนดภาษาจาวากล่าวว่าจำนวนเต็มที่มีขนาดเล็กกว่าของint
จะถูกแปลงเป็นint
ก่อนการคำนวณ (ถ้าหน่วยความจำของฉันทำหน้าที่ฉันอย่างถูกต้อง) และเป็นเหตุผลหนึ่งที่เลือกint
เมื่อไม่มีเหตุผลพิเศษ
ความยาวสูงสุดที่เวลารวบรวมคือสูงสุด 65536 โปรดทราบอีกครั้งว่าความยาวคือจำนวนไบต์ของการแทนUTF-8 ที่ปรับเปลี่ยนไม่ใช่จำนวนตัวอักษรในString
วัตถุ
String
วัตถุอาจสามารถมีอักขระมากขึ้นในขณะทำงาน อย่างไรก็ตามหากคุณต้องการใช้String
วัตถุด้วยDataInput
และDataOutput
อินเตอร์เฟสมันจะดีกว่าถ้าจะหลีกเลี่ยงการใช้String
วัตถุที่ยาวเกินไป ผมพบว่าข้อ จำกัด นี้เมื่อผมดำเนินการเทียบเท่า Objective-C ของและDataInput.readUTF()
DataOutput.writeUTF(String)
เนื่องจากอาร์เรย์ต้องมีการทำดัชนีด้วยจำนวนเต็มความยาวสูงสุดของอาร์เรย์คือInteger.MAX_INT
(2 31 -1 หรือ 2 147 483 647) นี่คือการสมมติว่าคุณมีหน่วยความจำเพียงพอที่จะเก็บอาร์เรย์ขนาดนั้นแน่นอน
ฉันมี iMac 2010 พร้อม RAM ขนาด 8GB ใช้ Eclipse Neon.2 Release (4.6.2) พร้อม Java 1.8.0_25 ด้วยอาร์กิวเมนต์ VM -Xmx6g ฉันรันโค้ดต่อไปนี้:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
try {
sb.append('a');
} catch (Throwable e) {
System.out.println(i);
break;
}
}
System.out.println(sb.toString().length());
ภาพพิมพ์นี้:
Requested array size exceeds VM limit
1207959550
ดังนั้นดูเหมือนว่าขนาดอาร์เรย์สูงสุดคือ ~ 1,207,959,549 จากนั้นฉันก็รู้ว่าเราไม่สนใจจริง ๆ ว่า Java มีหน่วยความจำไม่เพียงพอ: เราแค่มองหาขนาดอาเรย์สูงสุด (ซึ่งดูเหมือนจะเป็นค่าคงที่ที่ใดที่หนึ่ง) ดังนั้น:
for (int i = 0; i < 1_000; i++) {
try {
char[] array = new char[Integer.MAX_VALUE - i];
Arrays.fill(array, 'a');
String string = new String(array);
System.out.println(string.length());
} catch (Throwable e) {
System.out.println(e.getMessage());
System.out.println("Last: " + (Integer.MAX_VALUE - i));
System.out.println("Last: " + i);
}
}
สิ่งที่พิมพ์:
Requested array size exceeds VM limit
Last: 2147483647
Last: 0
Requested array size exceeds VM limit
Last: 2147483646
Last: 1
Java heap space
Last: 2147483645
Last: 2
ดังนั้นดูเหมือนว่า max คือ Integer.MAX_VALUE - 2 หรือ (2 ^ 31) - 3
ป.ล. ฉันไม่แน่ใจว่าทำไมStringBuilder
maxed out 1207959550
ของฉันในขณะที่char[]
maxed out ที่ (2 ^ 31) -3 ดูเหมือนว่าAbstractStringBuilder
ขนาดของภายในchar[]
จะเพิ่มขึ้นเป็นสองเท่าดังนั้นอาจเป็นสาเหตุของปัญหา
เห็นได้ชัดว่ามันถูกผูกไว้กับ int ซึ่งเป็น 0x7FFFFFFF (2147483647)
ประเภทการกลับมาของความยาว () วิธีการของคลาส String เป็นint
ความยาว int สาธารณะ ()
อ้างอิงhttp://docs.oracle.com/javase/7/docs/api/java/lang/String.html#length ()
ดังนั้นค่าสูงสุดของ int เป็น2147483647
String ถือเป็น char char ภายใน, ดังนั้นการสร้างดัชนีจึงอยู่ในช่วงสูงสุด ซึ่งหมายความว่าเราไม่สามารถจัดทำดัชนีสมาชิก 2147483648 ได้ดังนั้นความยาวสูงสุดของ String ใน java คือ 2147483647
ประเภทข้อมูลดั้งเดิม int คือ 4 ไบต์ (32 บิต) ใน java.As 1 บิต (MSB) ถูกใช้เป็นบิตสัญญาณช่วงถูก จำกัด ภายใน-2 ^ 31 ถึง 2 ^ 31-1 (-2147483648 ถึง 2147483647) เราไม่สามารถใช้ค่าลบสำหรับการจัดทำดัชนีดังนั้นช่วงที่เราสามารถใช้ได้คือตั้งแต่ 0 ถึง 2147483647
ตามที่กล่าวไว้ในคำตอบของ Takahiko Kawasaki , java แสดงถึง Unicode strings ในรูปแบบของUTF-8 ที่ดัดแปลงและใน JVM-Spec CONSTANT_UTF8_info โครงสร้าง JVM , 2 ไบต์จะถูกจัดสรรให้มีความยาว
เพื่อขยายคำตอบวิธีการของห้องสมุดASM jvm bytecodeประกอบด้วย:putUTF8
public ByteVector putUTF8(final String stringValue) {
int charLength = stringValue.length();
if (charLength > 65535) {
// If no. of characters> 65535, than however UTF-8 encoded length, wont fit in 2 bytes.
throw new IllegalArgumentException("UTF8 string too large");
}
for (int i = 0; i < charLength; ++i) {
char charValue = stringValue.charAt(i);
if (charValue >= '\u0001' && charValue <= '\u007F') {
// Unicode code-point encoding in utf-8 fits in 1 byte.
currentData[currentLength++] = (byte) charValue;
} else {
// doesnt fit in 1 byte.
length = currentLength;
return encodeUtf8(stringValue, i, 65535);
}
}
...
}
แต่เมื่อทำการแมปรหัสจุด> 1byte มันเรียกencodeUTF8
วิธี:
final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength /*= 65535 */) {
int charLength = stringValue.length();
int byteLength = offset;
for (int i = offset; i < charLength; ++i) {
char charValue = stringValue.charAt(i);
if (charValue >= 0x0001 && charValue <= 0x007F) {
byteLength++;
} else if (charValue <= 0x07FF) {
byteLength += 2;
} else {
byteLength += 3;
}
}
...
}
ในแง่นี้ความยาวสตริงสูงสุดคือ 65535 ไบต์เช่นความยาวการเข้ารหัส utf-8 และไม่char
นับ
คุณสามารถค้นหาช่วงรหัสจุดที่ได้รับการแก้ไขของ JVM ได้จากลิงก์ utf8 struct ด้านบน
String
หลักทฤษฏีInteger.MAX_VALUE
ความยาวของสตริงตัวอักษรในแหล่งที่ปรากฏจะถูก จำกัด เพียง65535 ไบต์ไบต์ของข้อมูล UTF-8