📜  Java substring() 方法内存泄漏问题及修复

📅  最后修改于: 2022-05-13 01:55:22.559000             🧑  作者: Mango

Java substring() 方法内存泄漏问题及修复

String是Java中的一个特殊类。 substring()String类中广泛使用的方法之一。它用于提取字符串的一部分,并具有两个重载变体:
1. 子字符串(int beginIndex)
此方法用于提取从beginIndex开始的部分字符串。
例子:

Java
class GFG {
    public static void main(String args[]) {
        String s = "geeksforgeeks";
        String subString = s.substring(4);
        System.out.print(subString);
    }
}


Java
class GFG {
    public static void main(String args[]) {
        String s = "geeksforgeeks";
        String subString = s.substring(5, 13);
        System.out.print(subString);
    }
}


Java
//JDK 7
public String(char value[], int offset, int count) {
    //check boundary
    this.value = Arrays.copyOfRange(value, offset, offset + count);
}
   
public String substring(int beginIndex, int endIndex) {
    //check boundary
    int subLen = endIndex - beginIndex;
    return new String(value, beginIndex, subLen);
}


beginIndex参数必须在 source 字符串的范围内,否则您会看到以下异常:

2. substring(int beginIndex, int endIndex)
此变体接受两个参数beginIndexendIndex 。它从beginIndexendIndex – 1中断字符串。
例子:

Java

class GFG {
    public static void main(String args[]) {
        String s = "geeksforgeeks";
        String subString = s.substring(5, 13);
        System.out.print(subString);
    }
}

substring() 如何在内部工作
我们都知道Java中的String是字符序列。 String 在内部由字符数组表示,当创建新的 String 对象时,它具有以下字段。

  • char value[] –字符数组
  • int count – 字符串中的总字符数
  • int offset –字符数组中的起始索引偏移量

当我们从原始字符串中获取字符串时,将在常量池或堆中创建新的 String 对象。 value[] char 数组将在两个 String 对象之间共享,但 String 对象的 count 和 offset 属性会根据子字符串长度和起始索引而有所不同。

JDK 6 中 substring() 引起的问题
此方法适用于小字符串。但是,当从具有更多字符的字符串中获取 substring() 时,如果您使用的是 JDK 6 或更低版本,则会导致严重的内存问题。
例子:


上面的 String 已经在堆中占用了大量内存。现在考虑我们需要来自 bigString 的前 2 个字符的场景。


现在我们不需要原来的字符串了。


我们可能认为bigString对象将被垃圾收集,因为我们将其设为null ,但我们的假设是错误的。当我们调用substring()时,会在内存中创建一个新的 String 对象。但它仍然引用原始字符串中的char[]数组值。这可以防止bigString进行垃圾收集过程,并且我们在内存中不必要地存储了100000 个字节(仅用于 2 个字符) 。可以在此处找到错误详细信息。
在 JDK 6 中处理 substring()
此问题应由开发人员处理。一种选择是从子字符串返回的字符串创建新的字符串对象。


现在,在Java堆中创建了新的 String 对象,拥有自己的 char[] 数组,最终原始 bigString 将符合垃圾收集过程。
其他选项是,在子字符串上调用intern()方法,然后从池中获取现有字符串或在必要时添加它。

修复 JDK 7 中的 substring()
Sun Microsystems 从 JdK 7 更改了 substring() 的实现。当我们在 JDK 7 中调用 substring() 时,jvm 不是从原始 String 引用 char[] 数组,而是使用自己的 char[] 数组创建新的 String 对象。

Java

//JDK 7
public String(char value[], int offset, int count) {
    //check boundary
    this.value = Arrays.copyOfRange(value, offset, offset + count);
}
   
public String substring(int beginIndex, int endIndex) {
    //check boundary
    int subLen = endIndex - beginIndex;
    return new String(value, beginIndex, subLen);
}


值得注意的是,在 JDK 7 中调用 substring() 方法时,会从内存中引用新的String对象,从而使原始字符串符合垃圾回收的条件。