论坛首页 Java企业应用论坛

优化变成了忧患:String类的split方法引起的内存泄漏

浏览 53057 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-03-29  
kimmking 写道
我也没看懂,hold的是引用,又不是char,怎么会有问题。


他并不是说所有split的小字符串都是从同一个大字符串下split出来的,不同的请求大字符串的来源不同,这样慢慢攒下来的。
1 请登录后投票
   发表时间:2010-03-29  
jarfield 写道
sdh5724 写道
我第一次这么仔细看一个贴子, 我读了6次。。。
每个String对象其实都指向了一个巨大的char[]  : 指向一个相同的内存, 会造成内存浪费? 
我很是不明白, 你一次输入的切割是多少数据, 如果你一次给了程序100M, 而你实际你只需要切割出1M的数据, 那么你说的问题是存在, 但是也仅仅是多引用了98M, 因为使用了内存共享。为了进一步降低内存使用, intern方法也是不错的, 可以避免相同的字符串被独立建立。

我想应该找找别的原因, 虽然你每次步骤是正确的, 但是你的结论我觉得不对。  使用split的地方非常多, 如果有这么严重的问题, java系统出大问题了。

在一个正常业务系统中char[] 占用70-90%的是非常正常的。
另外dump heap的时候, 最好带上live选项(非live大部分是分析应用动态情况下需要内存数量), 这样更能清楚的看到,是不是内存被长期暂用的部分。



读了6次,实在感动,呵呵!

“每个String对象其实都指向了一个巨大的char[]  : 指向一个相同的内存, 会造成内存浪费?”

这个地方我没有说清楚。我的程序是一个Web程序,每次接受请求,就会创建一个大的String对象,然后对该String对象进行split。如果接收了5W个请求,那么就会有5W个大String对象,因此会造成内存泄漏。我原以为缓存的是5W个小String,结果都是大String。

“用split的地方非常多, 如果有这么严重的问题, java系统出大问题了。 ”

我遇到的问题,不能算是split的bug。应该是,我不了解split的实现而误用了split。但是我的这种用法,应该也是比较直观的,不算什么奇技淫巧。所以,我最后感慨,还是要对JDK库的实现有所了解才行。

感谢live选项的建议


建议把这个补到主贴中,我刚开始也没搞清楚.
很好的一个实践,相信肯定会遇到类似情况.





0 请登录后投票
   发表时间:2010-03-29   最后修改:2010-03-29
JE帐号 写道
jarfield 写道
sdh5724 写道
我第一次这么仔细看一个贴子, 我读了6次。。。
每个String对象其实都指向了一个巨大的char[]  : 指向一个相同的内存, 会造成内存浪费? 
我很是不明白, 你一次输入的切割是多少数据, 如果你一次给了程序100M, 而你实际你只需要切割出1M的数据, 那么你说的问题是存在, 但是也仅仅是多引用了98M, 因为使用了内存共享。为了进一步降低内存使用, intern方法也是不错的, 可以避免相同的字符串被独立建立。

我想应该找找别的原因, 虽然你每次步骤是正确的, 但是你的结论我觉得不对。  使用split的地方非常多, 如果有这么严重的问题, java系统出大问题了。

在一个正常业务系统中char[] 占用70-90%的是非常正常的。
另外dump heap的时候, 最好带上live选项(非live大部分是分析应用动态情况下需要内存数量), 这样更能清楚的看到,是不是内存被长期暂用的部分。



读了6次,实在感动,呵呵!

“每个String对象其实都指向了一个巨大的char[]  : 指向一个相同的内存, 会造成内存浪费?”

这个地方我没有说清楚。我的程序是一个Web程序,每次接受请求,就会创建一个大的String对象,然后对该String对象进行split。如果接收了5W个请求,那么就会有5W个大String对象,因此会造成内存泄漏。我原以为缓存的是5W个小String,结果都是大String。

“用split的地方非常多, 如果有这么严重的问题, java系统出大问题了。 ”

我遇到的问题,不能算是split的bug。应该是,我不了解split的实现而误用了split。但是我的这种用法,应该也是比较直观的,不算什么奇技淫巧。所以,我最后感慨,还是要对JDK库的实现有所了解才行。

感谢live选项的建议


建议把这个补到主贴中,我刚开始也没搞清楚.
很好的一个实践,相信肯定会遇到类似情况.



已补充到帖子的末尾,多谢提醒。
0 请登录后投票
   发表时间:2010-03-29  
楼主很细心的人啊。

平时老想读读java源码的,不过太懒了,楼主把问题贴出来,对我这种懒人很有帮助,谢谢咯
0 请登录后投票
   发表时间:2010-03-29  
原来如此, 楼主没有把业务场景写明白, 这么说就很简单了。 
给楼主点建议:
1。 不要使用string的split, 每次会造成正则表达式的重新编译, 这是很消耗性能/内存的过程。 自己使用java的正则接口(如果表达式非常复杂, 我不建议使用java的正则接口,建议采用oro库)。
2。 使用String.intern()降低内存使用, 这个可以测试下, 我说的未必正确。 这个机制我还没有确切的文档给我解释是如何完成的。

Java里的string如果处理的很好, 将会带来内存/性能上质的飞跃。 我长期研究string处理的性能问题, 应用中涉及了大量的运算, 比如分词匹配,安全计算, XML的处理等等。




0 请登录后投票
   发表时间:2010-03-29   最后修改:2010-03-29
sdh5724 写道
原来如此, 楼主没有把业务场景写明白, 这么说就很简单了。 
给楼主点建议:
1。 不要使用string的split, 每次会造成正则表达式的重新编译, 这是很消耗性能/内存的过程。 自己使用java的正则接口(如果表达式非常复杂, 我不建议使用java的正则接口,建议采用oro库)。
2。 使用String.intern()降低内存使用, 这个可以测试下, 我说的未必正确。 这个机制我还没有确切的文档给我解释是如何完成的。

Java里的string如果处理的很好, 将会带来内存/性能上质的飞跃。 我长期研究string处理的性能问题, 应用中涉及了大量的运算, 比如分词匹配,安全计算, XML的处理等等。



谢谢建议。

每次编译正则表达式,确实有些浪费。不过我这里就是一些简单的规则,而且对性能/内存不是非常关心,所以暂不考虑oro这样的第三方库。以后遇到瓶颈了,再进行评估。

intern的问题,在存在大量相同内容字符串的场景下会降低内存使用。在我的场景下,可能不会起到明显的作用。

很赞成你对String处理重要性的观点,通过观察heap dump,String和char[]占据了绝大部分内存和对象数量。我想也正是因为这样,SUN才这么实现split方法。
0 请登录后投票
   发表时间:2010-03-29  
惭愧,上面的这段substring()的代码我是有读过的,当时只是想着sun的优化做的还是不错,这样可以显著的提高split()/substring()的效率,毕竟可以避免char[]的复制。

不过没有想到这个东东会在后面造成问题,恩,想来这个问题是非常可怕的:任何常驻内存的cellection,只要里面放了string,只要这个string是通过split()/subtring()从大字符中得到的,那么上面的问题就会发生。问题的严重性只是取决于大字符的大小 * 小string的数量。在极端情况下,很容易将问题放大到不可收拾的地方,一个简单的算法:100K * 10000 = 1G内存!!
0 请登录后投票
   发表时间:2010-03-29  
skydream 写道
惭愧,上面的这段substring()的代码我是有读过的,当时只是想着sun的优化做的还是不错,这样可以显著的提高split()/substring()的效率,毕竟可以避免char[]的复制。

不过没有想到这个东东会在后面造成问题,恩,想来这个问题是非常可怕的:任何常驻内存的cellection,只要里面放了string,只要这个string是通过split()/subtring()从大字符中得到的,那么上面的问题就会发生。问题的严重性只是取决于大字符的大小 * 小string的数量。在极端情况下,很容易将问题放大到不可收拾的地方,一个简单的算法:100K * 10000 = 1G内存!!


呵呵,我之前也读过这些代码,也没看出什么问题。真是实践出真知啊!
0 请登录后投票
   发表时间:2010-03-29  
jarfield 写道
skydream 写道
惭愧,上面的这段substring()的代码我是有读过的,当时只是想着sun的优化做的还是不错,这样可以显著的提高split()/substring()的效率,毕竟可以避免char[]的复制。

不过没有想到这个东东会在后面造成问题,恩,想来这个问题是非常可怕的:任何常驻内存的cellection,只要里面放了string,只要这个string是通过split()/subtring()从大字符中得到的,那么上面的问题就会发生。问题的严重性只是取决于大字符的大小 * 小string的数量。在极端情况下,很容易将问题放大到不可收拾的地方,一个简单的算法:100K * 10000 = 1G内存!!


呵呵,我之前也读过这些代码,也没看出什么问题。真是实践出真知啊!


出问题的前提是将这些split后字符串放到了常驻内存的collection中,否则如果用完就回收就没有问题了。
而且问题需要放大才能体现出来,否则几个字符串不过几M的内存,也不至于出现问题,但是怎么也经不起*1000, *10000这样的放大啊!

想起来,平时写的那些常见的服务器端处理,比如类似session这样的,从请求的字符串中分析出一个类似sessionid之类的作为key,放到hashmap中保存起来再在后续的操作中作为关联用,都很可能遇到同样的问题,没有造成问题只是放大系数不够,还有可能稍后自己清理掉了。不过浪费内存是肯定的。
0 请登录后投票
   发表时间:2010-03-29  
恩,值得注意, split 出来的小对象仍引用原有的大对象, 在需要对小对象保存时, 尽管可能只是大对象中的一小个片段, 但整个大对象都被引用, 被保留下来了. 因此split 后重新创建一个全新的小String去保存, 原有的大String 将会被垃圾回收. 这样处理就对了. 楼主是否是这意思?

非常高兴楼主分享了这样一个发现.

我想, 如果SPLIT出来的小对象不保存, 那么, 这是最省内存的办法.在你这个场景中我们注意到这点, 采取了一些措施就能解决了, 窃以为这不是个BUG.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics