当前位置: 首页 > 科技观察

String的最大长度是多少?涉及的知识太多,不要错过!

时间:2023-03-19 13:25:25 科技观察

本文转载自微信公众号《程序新视野》,作者为二哥。转载本文请联系程序新视界公众号。前言看到“String的长度限制是多少”这道题是不是觉得无聊?确实,这就是我第一次看到它时的感觉。但是当我深入追溯这个问题的时候,我发现String本身的长度限制的意义并不重要,重要的是这个过程中会串联很多知识点,简直就是一个完美的问题.难怪在高层访谈中会出现类似的问题。本文将带你追寻String长度的限制。需要提醒读者的是,结论并不重要,重要的是分析过程和所涉及的知识储备。比如String的底层实现,int类型的取值范围,《Java虚拟机规范》,Java编译器源码实现等知识点很多。String源码追踪依赖于String类的长度限制。必须先看String的源码实现。这里,我们以目前使用最多的JDK8为例。String的底层实现在JDK9及以后发生了变化,可以参考文章《JDK9对String字符串的新一轮优化》。我们都知道String类提供了一个length方法,那么我们是不是可以通过这个方法直接知道String的最大长度呢?/***Returnsthelengthofthisstring.*ThelengththissequaltothenumberofUnicode*codeunitsinthestring.**@returnthelengthofthesequenceofcharactersrepresentedbythis*object.*/publicintlength(){returnvalue.length;}这里的文档确实没有指定最大长度是多少,但是我们可以从返回的结果类型中得到一些线索。结果类型为int,也就是说int的取值范围是限制之一。如果你知道int在正整数部分的取值范围是2^31-1就好了。不知道的可以查看Integer对应的包装类:publicfinalclassIntegerextendsNumberimplementsComparable{/***Aconstantholdingtheminimumvaluean{@codeint}can*have,-231.*/@NativepublicstaticfinalintMIN_VALUE=0x80000000;/***Aconstantholdingthemaximumvaluean{@codeint}can*have,231-1.*/@NativepublicstaticfinalintMAX_VALUE=0x7fffffff;//...}不管MIN_VALUE和MAX_VALUE的值或注释,表示int的取值范围。此时,计算出String的最大长度应该是:2^31-1=2147483647回到length方法,我们看到length的值是通过value获取的,在JDK8中value实现为char数组:publicfinalclassStringimplementsjava.io.Serializable,Comparable,CharSequence{/**Thevalueisusedforcharactersstorage.*/privatefinalcharvalue[];//...}Java中的内码(运行内存)中的char使用UTF16编码,一个char占两个字节.因此,您还需要将上面计算的值乘以2。此时的计算公式为:2^31-1=2147483647个16-bitUnicodecharacter2147483647*2=4294967294(Byte)4294967294/1024=4194303.998046875(KB)4194303.998046875/1024=4095.9999980926513671875(MB)4095.9999980926513671875/1024=3.99999999813735485076904296875(GB)也也就是说,一个字符串占用的最大内存空间约等于4GB。但是这时候如果你声明一个长度为100000的字符串,你会发现编译器会抛出异常,提示信息如下:Error:Theconstantstringistoolong,isn'tthepromised21亿?为什么是十万?有例外吗?其实这个异常是编译期的限制决定的。字符串常量池的编译时限制了解过JVM虚拟机的朋友一定知道,当字符串通过字面量声明时,编译后会以常量的形式进入Class常量池。Strings="程序新视野";并且常量池对String的长度有限制。常量池中的每个数据项都有自己的类型。Java中的UTF-8编码的Unicode字符串在常量池中表示为CONSTANT_Utf8类型。在《Java虚拟机规范》中,可以看到String是由CONSTANT_String_info定义的。可见“string_index项的值必须是常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构”。继续看CONSTANT_Utf8_info的定义:length表示bytes[]数组的长度,类型为u2。u2的定义也可以在《Java虚拟机规范》中找到:u2表示两个字节的无符号数,1字节有8位,2字节有16位。因此,u2所能表示的最大值为2^16-1=65535。至此,第二个限制已经得出,即Class文件中常量池的格式规定了其字符串常量的长度不能超过65535。此时,如果你试图通过字面量来声明一个长度为65535的字符串:Strings="8888...8888";//有65535万个字符"8"编译器也会抛出同样的例外。为什么是这样?我们也可以从《Java虚拟机规范》(4.7.3小节)中找到这个问题的答案:本来是为了弥补早期设计的一个bug,“长度刚好是65535字节,长度是1字节的长度指令结束,异常处理程序无法处理该指令”,因此数组的最大长度被限制为65534。如果你可以查看JVM编译器部分的源代码,你可以看到代码实现Gen类中的此限制:/**Checkaconstantvalueandreportifitisastringthatis*toollarge.*/privatevoidcheckStringConstant(DiagnosticPositionpos,ObjectconstValue){if(nerrs!=0||//onlycomplainaboutalongstringonceconstValue==null||!(constValueinstanceofString)||((String)constValue).length()