最近我在审查一些Java Swing代码时看到了这个:
byte[] fooReference;
String getFoo() {
returns new String(fooReference);
}
void setFoo(String foo) {
this.fooReference = foo.getBytes();
}
据称,以上方法可以帮助你节省内存空间。这种方式是否太过于繁琐?还有其他人会这样封装他们的字符串吗?
最近我在审查一些Java Swing代码时看到了这个:
byte[] fooReference;
String getFoo() {
returns new String(fooReference);
}
void setFoo(String foo) {
this.fooReference = foo.getBytes();
}
据称,以上方法可以帮助你节省内存空间。这种方式是否太过于繁琐?还有其他人会这样封装他们的字符串吗?
这是一个非常糟糕的想法,请勿使用平台默认编码。如果你调用 setFoo
然后再调用 getFoo
,就没有保证你能获得相同的数据。
如果你必须要这样做,那么请使用UTF-8编码,它能够表示Unicode的全部字符...但我真的不会这样做。这种做法可能会节省一些内存,但大多数时间都会进行不必要的转换,容易出现错误,因为可能无法使用适当的编码。
我敢说有一些应用程序可以采用这种方法,但对于99.99%的应用程序来说,这是个可怕的想法。
这并不是很有用:
1. 每次调用getFoo或setFoo时都会复制该字符串,因此增加了CPU和内存的使用
2. 它很难理解
一次小小的历史考察...
在 Java 早期(1.0/1.1),如果你能确保不需要使用 ISO-8859-1 以外的字符集,使用字节数组而非字符串对象实际上有一些相当大的优势。在那个时代的虚拟机中,使用 drawBytes() 要比使用 drawString() 快 10 倍以上,它实际上可以节省内存,而当时内存仍然非常稀缺,applet 的硬编码内存限制为 32 MB 或 64 MB。与嵌入式 char[] 相比,byte[] 更小,而且还可以节省相对较重的 String 对象本身,如果有很多短字符串,这会产生相当大的差异。此外,访问纯 byte 数组也比使用 String 的访问器方法更快,后者有额外的边界检查。
但是,自从 Java 1.2 后,drawBytes 不再比字符串快,而且当前的 JIT 比 Symantec JIT 好得多,因此字节数组相对于字符串的微小性能优势已经不值得麻烦了。内存优势仍然存在,因此在某些极端情况下仍可能是一个选择,但现在如果没有必要,就不应该考虑使用它。
这可能会有些过度,甚至会消耗更多的内存,因为现在你有了两个字符串副本。实际字符串的生命周期取决于客户端,但像许多这样的黑科技一样,它很像过早优化。
这确实没有任何意义。如果它是一个编译时常量,你不需要将其转换为 String
,那么它会更有意义。你仍然有字符编码问题。
如果它是一个 char[]
常量,那么对我来说会更有意义。在现实世界中,有几个 JSP 编译器可以将字符串常量优化成一个 char[]
,然后可以轻松地写入到 Writer#write(char[])
中。最终这样会 "稍微" 更有效率,但在像 Google 搜索等大型和高度使用的应用程序中,这些小细节都很重要。
Tomcat 的 JSP 编译器 Jasper 也是这样做的。检查 genStringAsCharArray
设置。它会这样做:
static final char[] text1 = "some static text".toCharArray();
替代
static final String text1 = "some static text";
这样可以减少开销。它不需要一个完整的String
实例来围绕这些字符。
每次调用getFoo()都会实例化一个新的String。这样怎么能节省内存呢?如果有什么问题,当这些新引用变得无引用时,你会为垃圾收集器增加额外的开销来清理这些新实例。