我们一般并不会用到过长的字符串,但是实际上 String 可存储字符串的长度是有约束的,问题来了:
- 最长是多少?
- 为什么?
小 tip:注意区分String字面量和String变量。
更多问答 >>
-
2020-04-13 23:58
-
每日一问 | “必须在UI线程才能更新控件/界面” 这句人人皆知的话,100%正确吗?
2020-04-29 01:14 -
每日一问 “PathClassLoader 只能加载已安裝到系統中(即/data/app目录下)的apk文件” 严谨吗?
2020-05-05 20:46 -
2020-05-07 10:02
-
每日一问 | Activity 调用了finish()方法会立即调用onDestory()吗?
2020-05-13 00:16 -
每日一问 | 上周出现了大规模的github证书不可用的状态...但是真的是github服务器被攻击了么?
2020-04-01 21:49 -
每日一问 | Fragment 是如何被存储与恢复的? 有更新
2020-06-07 09:01 -
每日一问 | Activity 启动动画对页面打开速度有影响吗?
2020-04-22 22:06 -
每日一问 | 自定义控件测量模式真的和 match_parent,wrap_content 一一对应吗?
2020-03-30 01:01 -
2020-03-23 23:45
这个问题看似简单,其实暗藏杀机。
看到问题的第一闪念就是打开源码,找一找有无长度限制代码。很抱歉,那是没有的。然后区分一下字符串的类型,字面量与变量,jvm 对这两者有着截然不同的处理方式。字面量
字面量我的理解就是在程序编译之前就一定指定了值的变量,当然只能限定于基本类型以及字符串,代码中一般是这样的:
String s = "字面量";int a = 100;JVM 的对字面量的处理
jvm 会将这些字面量存储在运行时数据区的方法区的常量池中,那么字面量类型的字符串的长度限制就是字符串常量池大小的限制了。
常量池大小
字符串常量池使用 CONSTANT_UTF8_INFO 类型存储字符串字面量,大小限制是无符号的 16 位整数,因此理论上允许最大长度为 65536 字节。
ps:utf-8 一个中文占三个字节,就是理论最多能存 21845.333 个中文。变量
也就是 new 出来的对象,比如从 IO 读取来的,注意:编译期之前 new String() 会被处理成字面量。
变量类的长度限制就是 String 内部用于存储的数组的长度限制了,也就是 Int 的最大值。这一题是真冷,不过我昨天看见洋神分享过一篇文章:字符串String的最大长度 - 个人文章,23333
另外指出一下这篇文章的一些问题:不考虑硬件的情况下,java里能够直接new出来的数组的最大长度是Integer.MAX_VALUE?
答:最理想的情况是这样,但是实际上虚拟机对于数组长度还有额外的限制。在我这里(Windows 10,Java 13,jshell)上,执行以下代码可以正常分配数组:但是把length改成
Integer.MAX_VALUE - 1
或者Integer.MAX_VALUE
都会直接收到这样一条报错:原因就是数组大小超过了虚拟机的限制。
String内部是以char数组的形式存储?
以前确实是这样的,但某个版本后改成了byte[](至少在Java13上是这样)。编码的影响?
一个字符(不是指char)需要占用的大小由编码决定,比如我们常用的utf-8,英文字符占用1字节,中文汉字需要占用3~4字节;而如果你用utf-16的话,一个字符固定为占用4字节。也就是说,能存多长的字符串是由编码决定的,实际上很可能存不到那么多就爆了。总结一下:
编译期字符串常量:因为class格式的限制,最多能存2^16-1=65535字节。运行时:String内部通过数组表示,所以受限于虚拟机对数组长度的限制,最理想的情况下:如果是通过byte[]存储,那么最长可以表示Integer.MAX_VALUE=2147483647字节;如果是char[],java里一个char是两个字节,那么最长是Integer.MAX_VALUE*2=4294967294字节。1.用字面量声明的字符串在class字节码文件中是以CONSTANT_urf8_info格式存储的存储在常量池中,
CONSTANT_urf8_info{ u1 tag; u2 length; u1 bytes[length];}CONSTANT_urf8_info表结构中 有length属性代表这个表中字节数组的最大长度,u2是两个字节,因此一个字符串最大长度也就是u2所能代表的最大值65536个,但是需要使用2个字节来保存 null 值,因此一个字符串的最大长度为 65536 - 2 = 65534个字节 。2.在运行期间,String内部是以char数组的 value 存储的,数组的长度是int类型的 count,那么String允许的最大长度就是Integer.MAX_VALUE(2147483647) 了。java中一个char占2个字节,也就是16位。2147483647*16/8/1024/1024/1024 =4G,所以运行时大概需要约4GB的内存才能存储最大长度的字符串。都在说常量池的限制,U2 16位则理论上最大值限制65536,但是实验表明最多65534,理由是,判断的时候用的是 "<(2^16 -1)"
这是在前几天的一篇文章上看到的。就是下面那位仁兄提到的文章。https://segmentfault.com/a/1190000020381075
String被用作常量时,它被编译器当成字面量存放于常量池。常量池中主要存放两大类常量:字面量和符号引用;常量池中的每一个常量都是一个表,字符串常量存于CONSTANT_Utf8_info表(Tip:字节码文件由无符号数和表映射的数据构成,详见java虚拟机规范或者周志明老师的深入理解Java虚拟机)。
CONSTANT_Utf8_info表由tag、length和bytes组成。tag用于标识表的类型,大小为一个字节的无符号数;length表示使用utf-8缩略编码表示的字符串长度,大小为两个字节的无符号数;bytes存放被编码的字符串。字符串常量的最大值为2^16-1(65535)字节。当使用javac编译Test.java文件时,由于其中的字符串长度为65535,超出javac中规定的大小,编译失败;下面是含长度为65534的字符串的Test类编译前后的内容,位于0x7E~0x1007E(包括起止的两个字节)之间的数据为一个CONSTANT_Utf8_info表的映射,tag值为0x01(位于0x7E),之后的两个字节表示length值为0xFFFE(65534),随后的65534个字节字符串内容。javac无法编译字符串常量长度超过65534的java文件
原因:javac中对字符串常量长度做了限制,可以下面的
String被当作变量时,其长度由String存储方式决定。在JAVA SE 9之前,String内部是由char数组存储的,数组最大长度为Integer.MAX_VALUE(0x7fffffff),即2^31-1,并且char的取值范围在0~65535之间,占两个字节,因此String的最大长度为429496967294字节,运行时需要大约4GB的内存才能存储;JAVA SE 9及其后续版本将char数组改为byte数组,因此String的最大长度为2147483647字节。checkStringConstant
方法中看出。(Tip:javac源码位于OpenJDK中,并且各个版本路径不一定相同,修改上面的字节码文件,将length值由0xFFFE改为0xFFFF,并将bytes数据添加一个字节,执行java命令,程序正常运行。)