欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

Java内存泄漏-------JDK6与JDK7里的subString()方法,-------jdk6jdk7

来源: javaer 分享于  点击 6676 次 点评:107

Java内存泄漏-------JDK6与JDK7里的subString()方法,-------jdk6jdk7


在JVM中一般在GC时可达性分析算法,而有些java对象存在着“可达而不可用”,这也就导致java内存泄漏。

何为“可达而不可用”呢?

简单来说也就是一个对象生命周期已经结束,但是他被别的对象引用了,而那个对象生命周期还未结束,所以也就出现了内存泄漏。举个例子,对象A引用了对象B,对象A的生命周期比对象B长,当对象B生命周期结束时,对象A还未结束并且保持着对对象B的引用。对象B无法被GC,也就导致了内存泄漏。

String对象的解析

String对象实际上就是一个char数组,包括三个属性域:

private final char value[];  //字符数组
private final int offset;      //string对象在字符数组中的开始位置
private final int count;      //字符数组的长度 

JDK6中subString的实现

在JDK6中subString是是通过改变offset和count来创建一个新的String对象,value相当于一个仓库,还是用的父String的。

JDK6中对subString实现的源码:

public String substring(int beginIndex, int endIndex) {
  if (beginIndex < 0) {
      throw new StringIndexOutOfBoundsException(beginIndex);
  }
  if (endIndex > count) {
      throw new StringIndexOutOfBoundsException(endIndex);
  }
  if (beginIndex > endIndex) {
      throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
  }
  return ((beginIndex == 0) && (endIndex == count)) ? this :
      new String(offset + beginIndex, endIndex - beginIndex, value); 
    }
String(int offset, int count, char value[]) {
  this.value = value;
  this.offset = offset;
  this.count = count;
    }

JDK6中subString内存溢出实例:

String str1 = "abcdefg";
String str2 = str.substring(0, 2);
str1 = null;
内存变化图:

在这里,虽然str1为null,但是由于str1和str2之前指向的对象用的是同一个value数组,导致第一个String对象无法被GC,这也就是内存泄漏。

解决方案:

String str = "abcdefghijklmnopqrst";
String sub = str.substring(0, 2)+"";
str = null;
通过产生新的字符串对象,拥有新的value数组。
JDK7中subString的实现

在JDK7中,改进了subSyring方法,让它在方法内创建了一个新的char数组,用于存放value,也就解决了内存泄漏的问题。

源码如下:

public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
      throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
      throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
      throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
        : new String(value, beginIndex, subLen);
  }
public String(char value[], int offset, int count) {
    if (offset < 0) {
      throw new StringIndexOutOfBoundsException(offset);
    }
    if (count < 0) {
      throw new StringIndexOutOfBoundsException(count);
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
      throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
  }
public static char[] copyOfRange(char[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0)
            throw new IllegalArgumentException(from + " > " + to);
        char[] copy = new char[newLength];   //是创建了一个新的char数组
        System.arraycopy(original, from, copy, 0,
                         Math.min(original.length - from, newLength));
        return copy;
    }

内存变化如图:


如何避免java内存泄漏?

1、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在推出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄漏。
2、程序进行字符串处理时,尽量避免使用String,而应该使用StringBuffer。因为String类是不可变的,每一个String对象都会独立占用内存一块区域。
3、尽量少用静态变量。因为静态变量是全局的,存在方法区,GC不会回收。(用永久代实现的方法区,垃圾回收行为在这个区域是比较少出现的,垃圾回收器的主要目标是针对常量池和类型的卸载)
4、避免集中创建对象,尤其是大对象,如果可以的话尽量使用流操作。JVM会突然需要大量neicun,这时会出发GC优化系统内存环境
5、尽量运用对象池技术以提高系统性能。生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
6、不要在经常调用的方法中创建对象,尤其忌讳在循环中创建对象。可以适当的使用hashtable,vector创建一组对象容器,然后从容器中去取这些对象,而不用每次new之后又丢弃。
7、优化配置



相关文章

    暂无相关文章

用户点评