为什么jdk6中的substirng方法容易导致okhttp导致的内存泄漏漏

介绍 Java 中的内存泄漏
(点击上方公众号,可快速关注)来源:伯乐在线 - strongme,如需转载,发送「转载」二字查看说明Java语言的一个关键的优势就是它的内存管理机制。你只管创建对象,Java的垃圾回收器帮你分配以及回收内存。然而,实际的情况并没有那么简单,因为内存泄漏在Java应用程序中还是时有发生的。下面就解释下什么是内存泄漏,它为什么会发生,以及我们如何阻止它的发生。1. 什么是内存泄漏?内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着。要想理解这个定义,我们需要先了解一下对象在内存中的状态。下面的这张图就解释了什么是无用对象以及什么是未被引用对象。上面图中可以看出,里面有被引用对象和未被引用对象。未被引用对象会被垃圾回收器回收,而被引用的对象却不会。未被引用的对象当然是不再被使用的对象,因为没有对象再引用它。然而无用对象却不全是未被引用对象。其中还有被引用的。就是这种情况导致了内存泄漏。2. 为什么会发生内存泄漏?来先看看下面的例子,为什么会发生内存泄漏。下面这个例子中,A对象引用B对象,A对象的生命周期(t1-t4)比B对象的生命周期(t2-t3)长的多。当B对象没有被应用程序使用之后,A对象仍然在引用着B对象。这样,垃圾回收器就没办法将B对象从内存中移除,从而导致内存问题,因为如果A引用更多这样的对象,那将有更多的未被引用对象存在,并消耗内存空间。B对象也可能会持有许多其他的对象,那这些对象同样也不会被垃圾回收器回收。所有这些没在使用的对象将持续的消耗之前分配的内存空间。3. 如何防止内存泄漏的发生?下面是几条容易上手的建议,来帮助你防止内存泄漏的发生。特别注意一些像HashMap、ArrayList的集合对象,它们经常会引发内存泄漏。当它们被声明为static时,它们的生命周期就会和应用程序一样长。特别注意事件监听和回调函数。当一个监听器在使用的时候被注册,但不再使用之后却未被反注册。“如果一个类自己管理内存,那开发人员就得小心内存泄漏问题了。” 通常一些成员变量引用其他对象,初始化的时候需要置空。4. 一个小问题:为什么JDK6中的substirng()方法容易导致内存泄漏?要想解答上面的问题,你或许可以看看Substring() in JDK 6 and 7。/2013/09/the-substring-method-in-jdk-6-and-jdk-7/觉得本文对你有帮助?请分享给更多人关注「ImportNew」,提升Java技能java(30)
编程(25)
先来看一篇文章:
Java中的substring真的会引起内存泄露么?
androidyue ·
在Java中开发,String是我们开发程序可以说必须要使用的类型,String有一个substring方法用来截取字符串,我们想必也常常使用。但是你知道么,关于Java 6中的substring是否会引起内存泄露,在国外的论坛和社区有着一些讨论,以至于Java官方已经将其标记成bug,并且为此Java 7 还重新进行了实现。读到这里可能你的问题就来了,substring怎么会引起内存泄露呢?那么我们就带着问题,走进小黑屋,看看substring有没有内存泄露,又是怎么导致所谓的内存泄露。
substring方法提供两种重载,第一种为只接受开始截取位置一个参数的方法。
public String substring(int beginIndex)
比如我们使用上面的方法,”unhappy”.substring(2) 返回结果 “happy”
另一种重载就是接受一个开始截取位置和一个结束截取位置的参数的方法。
public String substring(int beginIndex, int endIndex)
使用这个方法,”smiles”.substring(1, 5) 返回结果 “mile”
通过这个介绍我们基本了解了substring的作用,这样便于我们理解下面的内容。
因为这个问题出现的情况在Java 6,如果你的Java版本号不是Java 6 需要调整一下。
终端调整(适用于Mac系统)
查看java版本号
13:03 $ java -version
java version “1.8.0_25”
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)
Ubuntu使用alternatives –config java,Fedora上面使用alternatives –config java。
如果你使用Eclipse,可以选择工程,右击,选择Properties(属性)— Java Compiler(Java编译器)进行特殊指定。
这里贴一下java官方bug里用到的重现问题的代码。
public class TestGC {
private String largeString = newString(newbyte[100000]);
String getString() {
returnthis.largeString.substring(0,2);
public static void main(String[] args) {
java.util.ArrayList list = newjava.util.ArrayList();
for(int i = 0; i & 1000000; i++) {
TestGC gc = newTestGC();
list.add(gc.getString());
然而上面的代码,只要使用Java 6 (Java 7和8 都不会抛出异常)运行一下就会报java.lang.OutOfMemoryError: Java heap space的异常,这说明没有足够的堆内存供我们创建对象,JVM选择了抛出异常操作。
于是有人会说,是因为你每个循环中创建了一个TestGC对象,虽然我们加入ArrayList只是两个字符的字符串,但是这个对象中又存储largeString这么大的对象,这样必然会造成OOM的。
然而,其实你说的不对。比如我们看一下这样的代码,我们只修改getString方法。
public class TestGC {
private String largeString = newString(newbyte[100000]);
String getString() {
//return this.largeString.substring(0,2);
returnnew String("ab");
public static void main(String[] args) {
java.util.ArrayList list = newjava.util.ArrayList();
for(int i = 0; i & 1000000; i++) {
TestGC gc = newTestGC();
list.add(gc.getString());
执行上面的方法,并不会导致OOM异常,因为我们持有的时1000000个ab字符串对象,而TestGC对象(包括其中的largeString)会在java的垃圾回收中释放掉。所以这里不会存在内存溢出。
那么究竟是什么导致的内存泄露呢?要研究这个问题,我们需要看一下方法的实现,即可。
深入Java 6实现
在String类中存在这样三个属性
1、value 字符数组,存储字符串实际的内容
2、offset 该字符串在字符数组value中的起始位置
3、count 字符串包含的字符的长度
Java 6中substring的实现
public String substring(int beginIndex, int endIndex) {
if(beginIndex & 0) {
thrownew StringIndexOutOfBoundsException(beginIndex);
if(endIndex & count) {
thrownew StringIndexOutOfBoundsException(endIndex);
if(beginIndex & endIndex) {
thrownew StringIndexOutOfBoundsException(endIndex - beginIndex);
return((beginIndex == 0) && (endIndex == count)) ? this:
newString(offset + beginIndex, endIndex - beginIndex, value);
上述方法调用的构造方法
//Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
this.value =
this.offset =
this.count =
当我们读完上述的代码,我们应该会豁然开朗,原来是这个样子啊!
当我们调用字符串a的substring得到字符串b,其实这个操作,无非就是调整了一下b的offset和count,用到的内容还是a之前的value字符数组,并没有重新创建新的专属于b的内容字符数组。
举个和上面重现代码相关的例子,比如我们有一个1G的字符串a,我们使用substring(0,2)得到了一个只有两个字符的字符串b,如果b的生命周期要长于a或者手动设置a为null,当垃圾回收进行后,a被回收掉,b没有回收掉,那么这1G的内存占用依旧存在,因为b持有这1G大小的字符数组的引用。
看到这里,大家应该可以明白上面的代码为什么出现内存溢出了。
共享内容字符数组
其实substring中生成的字符串与原字符串共享内容数组是一个很棒的设计,这样避免了每次进行substring重新进行字符数组复制。正如其文档说明的,共享内容字符数组为了就是速度。但是对于本例中的问题,共享内容字符数组显得有点蹩脚。
对于之前比较不常见的1G字符串只截取2个字符的情况可以使用下面的代码,这样的话,就不会持有1G字符串的内容数组引用了。
String littleString = newString(largeString.substring(0,2));
下面的这个构造方法,在源字符串内容数组长度大于字符串长度时,进行数组复制,新的字符串会创建一个只包含源字符串内容的字符数组。
public String(String original) {
int size = original.
char[] originalValue = original.
if(originalValue.length & size) {
// The array representing the String is bigger than the new
// String itself.
Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.
v = Arrays.copyOfRange(originalValue, off, off+size);
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalV
this.offset = 0;
this.count =
this.value =
Java 7 实现
在Java 7 中substring的实现抛弃了之前的内容字符数组共享的机制,对于子字符串(自身除外)采用了数组复制实现单个字符串持有自己的应该拥有的内容。
public String substring(int beginIndex, int endIndex) {
if(beginIndex & 0) {
thrownew StringIndexOutOfBoundsException(beginIndex);
if(endIndex & value.length) {
thrownew StringIndexOutOfBoundsException(endIndex);
int subLen = endIndex - beginI
if(subLen & 0) {
thrownew StringIndexOutOfBoundsException(subLen);
return((beginIndex == 0) && (endIndex == value.length)) ? this
:newString(value, beginIndex, subLen);
substring方法中调用的构造方法,进行内容字符数组复制。
public String(char value[], int offset, int count) {
if(offset & 0) {
thrownew StringIndexOutOfBoundsException(offset);
if(count & 0) {
thrownew StringIndexOutOfBoundsException(count);
// Note: offset or count might be near -1&&&1.
if(offset & value.length - count) {
thrownew StringIndexOutOfBoundsException(offset + count);
this.value = Arrays.copyOfRange(value, offset, offset+count);
真的是内存泄露么
我们知道了substring某些情况下可能引起内存问题,但是这个叫做内存泄露么?
其实个人认为这个不应该算为内存泄露,使用substring生成的字符串b固然会持有原有字符串a的内容数组引用,但是当a和b都被回收之后,该字符数组的内容也是可以被垃圾回收掉的。
哪个版本实现的好
关于Java 7 对substring做的修改,收到了褒贬不一的反馈。
个人更加倾向于Java 6的实现,当进行substring时,使用共享内容字符数组,速度会更快,不用重新申请内存。虽然有可能出现本文中的内存性能问题,但也是有方法可以解决的。
Java 7的实现不需要程序员特殊操作避免了本文中问题,但是进行每次substring的操作性能总会比java 6 的实现要差一些。这种实现显得有点“糟糕”。
问题的价值
虽然这个问题出现在Java 6并且Java 7中已经修复,但并不代表我们就不需要了解,况且Java 7的重新实现被喷的很厉害。
其实这个问题的价值,还是比较宝贵的,尤其是内容字符数组共享这个优化的实现。希望可以为大家以后的设计实现提供帮助和一些想法。
受影响的方法
trim和subSequence都存在调用substring的操作。Java 6和Java 7 substring实现的更改也间接影响到了这些方法。
以下三篇文章写得都比较不错,但是都稍微有一些问题,我都已经标明出来,大家阅读时,需要注意。
The substring() Method in JDK 6 and JDK 7 本文中解决java6中问题提到的字符串拼接不推荐,具体原因可以参考Java细节:字符串的拼接
How SubString method works in Java – Memory Leak Fixed in JDK 1.7 本文中提到的有一个概念错误,新的字符串不会阻止旧的字符串被回收,而是阻止旧字符串中的内容字符数组。阅读时需要注意。
JDK-4513622 : (str) keeping a substring of a field prevents GC for object 本文中提到的有一个测试,使用非new的形式有一点问题,其忽视了字符串常量池的存在,具体查看下面的注意。
============================
以上是别人写的文章
这里我要说的是,subString()由JDK6到JDK7的改动就是将原先的浅拷贝变成深拷贝,目的就是防止用户不规范的编码导致内存泄露,看似用心良苦,实则在打脸。为什么要这么说?
先来看看JDK8里面ArrayList对List接口的subList方法的实现:
public List subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
SubList(AbstractList parent,
int offset, int fromIndex, int toIndex) {
this.parent =
this.parentOffset = fromI
this.offset = offset + fromI
this.size = toIndex - fromI
this.modCount = ArrayList.this.modC
上面代码看出来,其实subList也是浅拷贝,和JDK6的subString的做法是一致的,
换句话说,如果subList使用不当也会造成内存泄露。这是JDK6以前对sub**方法的传统都是浅拷贝,只要稍微注意就好。
由于社区有人觉得这锅要官方解决,官方也是乖,就在JDK7把subString变成深拷贝,而subList依然是浅拷贝。
导致的结果就是,subList依然是当年的subList,而subString已经物是人非了。
至于我为什么发觉这个问题呢?因为我本来是一个比较懒得人,我看过subList的源码,没看过subString的源码,我会推测subList是浅拷贝,subString应该也是浅拷贝,这是合理的做法。然后现在我再也不能那么轻狂的自以为是了,遇到没把握的还是得看看源码,能少一个坑是一个。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:12590次
排名:千里之外
原创:78篇
转载:19篇
评论:10条
(7)(7)(19)(49)(3)(1)(1)(5)(2)(3)»»介绍Java中的内存泄漏
  Java语言的一个关键的优势就是它的内存管理机制。你只管创建对象,Java的垃圾回收器帮你分配以及回收内存。然而,实际的情况并没有那么简单,因为内存泄漏在Java应用程序中还是时有发生的。
  下面就解释下什么是内存泄漏,它为什么会发生,以及我们如何阻止它的发生。
 1. 什么是内存泄漏?
  内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着。
  要想理解这个定义,我们需要先了解一下对象在内存中的状态。下面的这张图就解释了什么是无用对象以及什么是未被引用对象。
Unused and Unreferenced
  上面图中可以看出,里面有被引用对象和未被引用对象。未被引用对象会被垃圾回收器回收,而被引用的对象却不会。未被引用的对象当然是不再被使用的对象,因为没有对象再引用它。然而无用对象却不全是未被引用对象。其中还有被引用的。就是这种情况导致了内存泄漏。
 2. 为什么会发生内存泄漏?
  来先看看下面的例子,为什么会发生内存泄漏。下面这个例子中,A对象引用B对象,A对象的生命周期(t1-t4)比B对象的生命周期(t2-t3)长的多。当B对象没有被应用程序使用之后,A对象仍然在引用着B对象。这样,垃圾回收器就没办法将B对象从内存中移除,从而导致内存问题,因为如果A引用更多这样的对象,那将有更多的未被引用对象存在,并消耗内存空间。
  B对象也可能会持有许多其他的对象,那这些对象同样也不会被垃圾回收器回收。所有这些没在使用的对象将持续的消耗之前分配的内存空间。
Object-Life-Time
 3. 如何防止内存泄漏的发生?
  下面是几条容易上手的建议,来帮助你防止内存泄漏的发生。
特别注意一些像HashMap、ArrayList的集合对象,它们经常会引发内存泄漏。当它们被声明为static时,它们的生命周期就会和应用程序一样长。
特别注意事件监听和回调函数。当一个监听器在使用的时候被注册,但不再使用之后却未被反注册。
&如果一个类自己管理内存,那开发人员就得小心内存泄漏问题了。& 通常一些成员变量引用其他对象,初始化的时候需要置空。
 4. 一个小问题:为什么JDK6中的substirng()方法容易导致内存泄漏?
  要想解答上面的问题,你或许可以看看。
  外文链接:
验证消息:Admin10000
提示:更多精彩内容关注微信公众号:全栈开发者中心(admin10000_com)
理智评论文明上网,拒绝恶意谩骂 发表评论 / 共0条评论
登录会员中心转过路角忽然发现,3岁的儿子已在路口等着自己回来。
在0℃的江苏无锡街头,环卫工用双手疏通下水道。
声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
(点击上方公众号,可快速关注)
  来源:伯乐在线 - strongme,
  如需转载,发送「转载」二字查看说明
  Java语言的一个关键的优势就是它的内存管理机制。你只管创建对象,Java的垃圾回收器帮你分配以及回收内存。然而,实际的情况并没有那么简单,因为内存泄漏在Java应用程序中还是时有发生的。
  下面就解释下什么是内存泄漏,它为什么会发生,以及我们如何阻止它的发生。
  1. 什么是内存泄漏?
  内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着。
  要想理解这个定义,我们需要先了解一下对象在内存中的状态。下面的这张图就解释了什么是无用对象以及什么是未被引用对象。
  上面图中可以看出,里面有被引用对象和未被引用对象。未被引用对象会被垃圾回收器回收,而被引用的对象却不会。未被引用的对象当然是不再被使用的对象,因为没有对象再引用它。然而无用对象却不全是未被引用对象。其中还有被引用的。就是这种情况导致了内存泄漏。
  2. 为什么会发生内存泄漏?
  来先看看下面的例子,为什么会发生内存泄漏。下面这个例子中,A对象引用B对象,A对象的生命周期(t1-t4)比B对象的生命周期(t2-t3)长的多。当B对象没有被应用程序使用之后,A对象仍然在引用着B对象。这样,垃圾回收器就没办法将B对象从内存中移除,从而导致内存问题,因为如果A引用更多这样的对象,那将有更多的未被引用对象存在,并消耗内存空间。
  B对象也可能会持有许多其他的对象,那这些对象同样也不会被垃圾回收器回收。所有这些没在使用的对象将持续的消耗之前分配的内存空间。
  3. 如何防止内存泄漏的发生?
  下面是几条容易上手的建议,来帮助你防止内存泄漏的发生。
特别注意一些像HashMap、ArrayList的集合对象,它们经常会引发内存泄漏。当它们被声明为static时,它们的生命周期就会和应用程序一样长。
特别注意事件监听和回调函数。当一个监听器在使用的时候被注册,但不再使用之后却未被反注册。
“如果一个类自己管理内存,那开发人员就得小心内存泄漏问题了。” 通常一些成员变量引用其他对象,初始化的时候需要置空。
  4. 一个小问题:为什么JDK6中的substirng()方法容易导致内存泄漏?
  要想解答上面的问题,你或许可以看看Substring() in JDK 6 and 7。
  /2013/09/the-substring-method-in-jdk-6-and-jdk-7/
觉得本文对你有帮助?请分享给更多人
关注「ImportNew」,提升Java技能
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。
请先登录再操作
请先登录再操作
微信扫一扫分享至朋友圈
搜狐公众平台官方账号
生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者
搜狐网教育频道官方账号
全球最大华文占星网站-专业研究星座命理及测算服务机构
主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓
主演:尚格?云顿/乔?弗拉尼甘/Bianca Bree
主演:艾斯?库珀/ 查宁?塔图姆/ 乔纳?希尔
baby14岁写真曝光
李冰冰向成龙撒娇争宠
李湘遭闺蜜曝光旧爱
美女模特教老板走秀
曝搬砖男神奇葩择偶观
柳岩被迫成赚钱工具
大屁小P虐心恋
匆匆那年大结局
乔杉遭粉丝骚扰
男闺蜜的尴尬初夜
客服热线:86-10-
客服邮箱:}

我要回帖

更多关于 信息泄漏可导致后果 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信