String字符串的最大长度

Java String的最大长度:编译与运行期解析
本文探讨了Java中String字符串的最大长度,分别从编译期(Javac与Eclipse JDT编译器的区别)和运行期进行分析。在Javac下,String的最大长度为65534,而Eclipse由于其编译器优化,能够编译更长的字符串。运行期String的长度受制于内存,约为4GB。

开篇三个问题

作为Java的程序员,不知道在Java代码中定义了多少个字符串,可是看看下面3个问题。你是否认真思考过?是否动手实践过?

1.Java中的字符串String的最大长度是多少?
2.Eclipse使用哪种Java编译器?
3.为何Eclipse要出自己的编译器?

对于字符串可以承受的最大长度,要分为2个阶段,一个是编译时期(也就是你代码定义了一个String字符串,String s= "xiaohu"),一个是运行时期(指在程序运行过程中)

编译期String字符串的限制

我们都知道JVM里面是包含常量池的,(是一种对字符串的性能优化,不用反复创建新的字符串了)当我们使用字符串字面量直接定义String的时候,是会把字符串在常量池中存储一份的。常量池中的每一项常量都是一个表,都有自己对应的类型。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8_info类型表,结构如下:

CONSTANT_Utf8_info型常量的结构
类型    名称  数量
u1    tag 1
u2    length  1
u1    bytes   length

u2类型的length的值就表明了这个UTF-8编码字符串长度是多少字节。所以CONSTANT_Utf8_info型常量对应的最大长度也就是java中UTF-8编码的字符串的长度,顺便提一下Class文件中的方法和字段也是引用CONSTANT_Utf8_info型常量来描述名称的。u2是无符号的16位整数,因此理论上允许的的最大长度是2^16-1=65535

编译器javac下String的长度

创建一个测试类

public class TestStr {
    public static void main(String[] args{
             String LongStr ="aaaa..."//一共65535个a
             System.out.println(LongStr.length());
     }
}

使用javac命令编译它。编译报错。相应目录没有生成对应的TestStr.class文件
去除一个字符串,使用65534个字符串。

public class TestStr {
    public static void main(String[] args{
             String LongStr ="aaaa..."//一共65534个a
             System.out.println(LongStr.length());
     }
}

javac命令编译它。编译正常。相应目录生成对应的TestStr.class文件

我们在看看Oracle JDK的编译工具Javac内部,javac也是java写的。

/** Check a constant value and report if it is a string that is
 *  too large.
 */

private void checkStringConstant(DiagnosticPosition pos, Object constValue) {
    if (nerrs != 0 || // only complain about a long string once
        constValue == null ||
        !(constValue instanceof String) ||
        ((String)constValue).length() < Pool.MAX_STRING_LENGTH)
        return;
    log.error(pos, "limit.string");
    nerrs++;
}
...
在看看Pool.MAX_STRING_LENGTH

public class Pool {

    ...

    public static final int MAX_STRING_LENGTH = 0xFFFF;

    ...
}

通过上边代码可以看到 MAX_STRING_LENGTH = 0xFFFF 而 0xFFFF 是十进制的 65535。但是上面我们得出的结果是Javac编译下最大长度是65534,是因为 Javac 源码中做的限制是((String)constValue).length() < Pool.MAX_STRING_LENGTH) 注意是 < 而不是 <= , 小于65535那自然最多只能是65534了。

但是U2类型能表达的最大值是65535。上面65535个长度的字符串在javac下报错了是受到了javac编译器的限制了。如果你在上面65534长度生成的TestStr.class中手动在添加一个字符串(注意是在javac编译后的class文件中添加)是可以得到65535长度的结果。

总结一下:在Javac编译器下,字符串String的最大长度限制也即是U2类型所能表达的最大长度65534。避开javac最大长度是65535?

Eclise的JDT编译器下String的长度
Eclipse有自己的Java编译器,称为[JDT Core] [2](org.eclipse.jdt.core)。并不是用的javac编译器。
创建一个测试类

public class TestStr {
    public static void main(String[] args{
             String LongStr ="aaaa..."//一共65540个a
             System.out.println(LongStr.length());
     }
}

发现Eclipse执行可正常执行。这肯定是Eclise的JDT编译器做了手脚。果然通过在Eclipse工作空间下找到了其编译生成的TestStr.class。使用javap命令查看

   6:   invokespecial   #20//Method java/lang/StringBuilder."<init>":(Ljava/la
ng/String;)V
   9:   ldc     #23//String QyNDAbAgIGqQIBAQ1
   11:  invokevirtual   #25//Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   14:  invokevirtual   #29//Method java/lang/StringBuilder.toString:()Ljava/l
ang/String;
   17:  invokevirtual   #33//Method java/lang/String.intern:()Ljava/lang/Strin
g;
   20:  astore_1
   21:  getstatic       #38//Field java/lang/System.out:Ljava/io/PrintStream;
   24:  aload_1
   25:  invokevirtual   #44//Method java/lang/String.length:()I
   28:  invokevirtual   #48//Method java/io/PrintStream.println:(I)V
   31:  return

}

上面我们就明白了之所以JDT能编译过,只是因为JDT优化为了StringBuilder的append。

Eclipse编译器本身包含在org.eclipse.jdt.core插件中。Eclipse不会使用任何用户安装的JDK。相反,由于以下主要原因,它使用自己的JDT核心来编译Java程序:
主要原因是JDT核心具有渐进式编译的能力,这意味着它会逐步编译代码中的更改(这也是Eclipse不需要编译按钮的原因,因为它会在检测到更改时自动编译)。但Oracle的JDK不支持增量编译。

运行期String的字符串限制

String内部是以char数组的形式存储,数组的长度是int类型,那么String允许的最大长度就是Integer.MAX_VALUE了。又由于java中的字符是以16位存储的,因此大概需要4GB的内存才能存储最大长度的字符串。

总结一下

1.Java中的字符串String最大长度,编译期如果是javac编译就是65534。如果绕过javac编译的限制,其最大长度可以达到u2类型变达的最大值65535。

2.Java中的字符串String最大长度运行期大约4G。

3.Eclise编译超过65534长度的字符串不报错,是Eclipse有自己的Java编译器。JDT优化为了StringBuilder的append。

4.Eclise使用自己的编译器。主要原因是JDT核心具有渐进式编译的能力,这意味着它会逐步编译代码中的更改(这也是Eclipse不需要编译按钮的原因,因为它会在检测到更改时自动编译)。但Oracle的JDK不支持增量编译。

在 Java 中,`String` 类型的最大长度限制主要受到以下几个因素的影响: 1. **编译时限制**:当 `String` 作为字符串常量出现在代码中时,其长度受限于 Java 字节码文件的结构。字符串常量池中的字符串常量在编译时会被存储为 `UTF-8` 编码的字节数组,其最大字节数为 `65535`,因此最大字符长度为 `65534`(因为需要一个字节作为终止符)[^2]。 2. **运行时限制**:在运行时,`String` 内部是以 `char[]` 数组的形式存储的,而数组的长度是 `int` 类型。因此,理论上 `String` 的最大长度为 `Integer.MAX_VALUE`,即 $2^{31} - 1$(2147483647)。不过,实际使用中还受到内存大小的限制,因为要存储如此长的字符串需要大约 4GB 的内存空间(每个 `char` 占用 2 字节)[^3]。 3. **JVM 限制**:虽然理论上 `String` 的最大长度可以达到 `Integer.MAX_VALUE`,但在实际运行过程中,字符串的长度还受限于当前虚拟机可用的内存大小。如果尝试创建一个接近最大长度字符串,可能会因内存不足而抛出 `OutOfMemoryError` 异常。 ### 总结 - **编译时常量最大长度**:65534 字符。 - **运行时最大长度**:理论上为 `Integer.MAX_VALUE`(2147483647),但受限于内存。 - **内存限制**:存储最大长度字符串需要大约 4GB 的内存空间。 ### 示例代码 以下是一个简单的示例,用于演示如何创建一个较长的字符串: ```java public class Main { public static void main(String[] args) { int maxLength = Integer.MAX_VALUE; // 理论上的最大长度 try { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100000; i++) { // 仅作为示例,实际创建如此长的字符串可能会导致内存不足 sb.append("a"); } String longString = sb.toString(); System.out.println("成功创建了一个较长的字符串,长度为:" + longString.length()); } catch (OutOfMemoryError e) { System.out.println("内存不足,无法创建如此长的字符串。"); } } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值