StringBuffer is dangerous when dealing with lonnnng Strings!!!
2015-06-04 23:38
344 查看
today we were forced to dive into the StringBuffer class - after looking into a OutOfMemory problem in our client.
It's "amazing" that the following code causes an OutOfMemory - event though the virtual machine is started with -Xmx256m:
Code:
A String with the length of 30.000.000 characters is added to a stringbuffer - that's it. It's even more astonishing that the error
is NOT thrown when appending the "big" string, but when appending "Hallo".
The reason is the internal processing of StirngBuffer. StringBuffer keeps everyhing in an char-array, and there is a certain method
that increases the char-array-size when finding out that the array does not fit anymore. The method increases the size by at least "2 * currentSize + 2". This may make sense for small sizes, but it's completely a wrong decision for big sizes. In our case the
appending of "Hello" means that the total array size is increased by 60.000.002 as minimum, so that the minimum size is 90.000.002 afterwards. And now, the previous char array with length of 30.000.000 needs to be copied into the one of 90.000.002 - this means
both arrays co-exist for a short duration of time, resulting in 120.000.002 characters being in memory (and of course there is some additional memory blocked by the array object). So the appending of "Hallo" means a lot... - enough to cause out of memory.
When concatenating long strings, then it is as consequence essential to carefully work with StringBuffers. We eliminated our OutOfMemory
problems by using the following utitlity method:
Code:
When now changing the test code to:
Code:
It's "amazing" that the following code causes an OutOfMemory - event though the virtual machine is started with -Xmx256m:
Code:
public static void main(String[] args) { try { char[] chars = new char[30000000]; for (int i=0; i<chars.length; i++) chars[i] = 'A'; String s = new String(chars); StringBuffer sb = new StringBuffer(); System.out.println("Now appending s"); sb.append(s); System.out.println("Now appending Hallo"); sb.append("Hallo!"); // ---- System.out.println("Finished!"); } catch (Throwable t) { t.printStackTrace(); } } OUTPUT: Now appending s Now appending Hallo java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2882) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390) at java.lang.StringBuffer.append(StringBuffer.java:224) at org.eclnt.client.ztest.TestMem.main(TestMem.java:22)
A String with the length of 30.000.000 characters is added to a stringbuffer - that's it. It's even more astonishing that the error
is NOT thrown when appending the "big" string, but when appending "Hallo".
The reason is the internal processing of StirngBuffer. StringBuffer keeps everyhing in an char-array, and there is a certain method
that increases the char-array-size when finding out that the array does not fit anymore. The method increases the size by at least "2 * currentSize + 2". This may make sense for small sizes, but it's completely a wrong decision for big sizes. In our case the
appending of "Hello" means that the total array size is increased by 60.000.002 as minimum, so that the minimum size is 90.000.002 afterwards. And now, the previous char array with length of 30.000.000 needs to be copied into the one of 90.000.002 - this means
both arrays co-exist for a short duration of time, resulting in 120.000.002 characters being in memory (and of course there is some additional memory blocked by the array object). So the appending of "Hallo" means a lot... - enough to cause out of memory.
When concatenating long strings, then it is as consequence essential to carefully work with StringBuffers. We eliminated our OutOfMemory
problems by using the following utitlity method:
Code:
public static String concatenateStrings(List<String> items) { if (items == null) return null; if (items.size() == 0) return ""; int expectedSize = 0; for (String item: items) expectedSize += item.length(); StringBuffer result = new StringBuffer(expectedSize); for (String item: items) result.append(item); return result.toString(); }
When now changing the test code to:
Code:
public static void main(String[] args) { try { char[] chars = new char[30000000]; for (int i=0; i<chars.length; i++) chars[i] = 'A'; String s = new String(chars); List<String> buffer = new ArrayList<String>(); System.out.println("Now appending s"); buffer.add(s); System.out.println("Now appending Hallo"); buffer.add("Hallo!"); String all = concatenateStrings(buffer); // ---- System.out.println("Finished!"); } catch (Throwable t) { t.printStackTrace(); }
相关文章推荐
- CSS3 icon font完全指南(CSS3 font 会取代icon图标)
- Java NIO系列教程(三) Buffer
- PhoneGap与Jquery Mobile组合开发android应用的配置
- 移动HTML5前端性能优化指南
- BootStrap 智能表单系列 首页 (持续更新中...)
- HTML插入CSS样式表方法总结
- js跳转页面,谷歌和IE跳转页面不一样!
- 【HTML5 WebSocket】WebSocket对象特性和方法
- Angular 学习笔记 2015.6.4
- Caffe-代码解析-SyncedMemory
- JSON解析工具-org.json
- jquery性能优化
- JavaScript概述
- 浅入浅出JS中的eval及json
- 深入浅出JSON
- 手机浏览器上传照片
- CSS3 可视化的格式模型
- 解决favicon.ico无法显示的问题
- jquery动画
- 基于 bootstrap 的数据展示--bootgrid 样式修改。