我正在浏览String类API,看起来像substring方法导致潜在的内存泄漏,因为它与原始String共享相同的字符数组.
如果原始字符串很大,则子字符串返回的小字符串可以防止原始字符串(由大数组备份)从Java中的垃圾收集.
任何想法或我读错了API.
我正在读取一个非常大的文件,并从每一行中提取一小部分文本.但是在操作结束时,我的内存很少.在读取文件后,垃圾收集器似乎无法释放内存.
我的问题是:有没有办法释放这段记忆?或者这是一个JVM错误?
我创建了一个SSCCE来证明这一点.它读取1 mb(由于16位编码而在Java中为2 mb)文件并从每行中提取一个字符(~4000行,因此应该是大约8 kb).在测试结束时,仍然使用了完整的2 mb!
最初的内存使用情况:
Allocated: 93847.55 kb
Free: 93357.23 kb
Run Code Online (Sandbox Code Playgroud)
读取文件后立即(在任何手动垃圾收集之前):
Allocated: 93847.55 kb
Free: 77613.45 kb (~16mb used)
Run Code Online (Sandbox Code Playgroud)
这是预料之中的,因为程序正在使用大量资源来读取文件.
然而,我垃圾收集,但不是所有的内存都被释放:
Allocated: 93847.55 kb
Free: 91214.78 kb (~2 mb used! That's the entire file!)
Run Code Online (Sandbox Code Playgroud)
我知道手动调用垃圾收集器不会给你任何保证(在某些情况下它是懒惰的).然而,这发生在我的大型应用程序中,其中文件几乎占用了所有可用内存,并且导致程序的其余部分尽管需要它而耗尽内存.这个例子证实了我怀疑从文件中读取的多余数据没有被释放.
以下是生成测试的SSCCE:
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws Throwable {
Runtime rt = Runtime.getRuntime();
double alloc = rt.totalMemory()/1000.0;
double free = rt.freeMemory()/1000.0;
System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);
Scanner in = new Scanner(new File("my_file.txt"));
ArrayList<String> …Run Code Online (Sandbox Code Playgroud) 在任何应用程序中,字符串都会消耗大量内存。每当垃圾收集器访问字符串对象时,它都会记录字符数组。它获取它们的哈希值并将其与对数组的弱引用一起存储。一旦找到另一个具有相同哈希码的字符串,它就会逐个字符地比较它们。如果它们也匹配,一个字符串将被修改并指向第二个字符串的字符数组。然后第一个字符数组不再被引用并且可以被垃圾收集。
字符串池:
java程序使用的所有字符串都存储在这里。如果两个变量被初始化为相同的字符串值。两个字符串不是在内存中创建的,内存中只会存储一个副本,并且都指向同一个内存位置。
因此,java 已经通过检查字符串池中是否存在字符串来处理不在堆中创建重复字符串的问题。那么字符串去重的目的是什么?
如果有如下代码
String myString_1 = new String("Hello World");
String myString_2 = new String("Hello World");
Run Code Online (Sandbox Code Playgroud)
即使它们相同,也会在内存中创建两个字符串。除了字符串重复数据删除有用之外,我想不出任何其他场景。显然我一定错过了一些东西。我错过了什么?
提前致谢
所以我的问题是关于声明和分配字符串.
我通常声明字符串的方式是执行以下操作:
String s1 = "Stackoverflow";
Run Code Online (Sandbox Code Playgroud)
然后,如果我需要更改s1的值,我会执行以下操作:
s1 = "new value";
Run Code Online (Sandbox Code Playgroud)
今天我找到了另一种方法,然后声明一个字符串就像:
String s2 = new String("Stackoverflow");
Run Code Online (Sandbox Code Playgroud)
然后更改值将是:
s2 = new String("new value");
Run Code Online (Sandbox Code Playgroud)
我的问题是两者之间有什么区别,还是只是优惠.从第四行查看代码
s2 = new String ("new value");
Run Code Online (Sandbox Code Playgroud)
我假设这样做会创建一个新的内存位置,然后s2指向它,所以我怀疑它会用于更改值,但我可以看到它在声明字符串时被使用.
我对这个例子感到困惑:
StringBuilder a = new StringBuilder("abcde");
String subStr = a.substring( a.indexOf("a") , a.indexOf("c") );
int leng = a.length();
char ch = a.charAt(4);
System.out.println( subStr + " " + leng + " " + ch);
//the result will be : subStr = abc , leng = 5 ch = e
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么ch = e并且它不会创建异常?
我的想法:
我有一个 StringBuilder,一个非不可变的对象,如果我在该对象上使用一种方法,它将返回一个具有相同引用对象的对象的新值。
a.substring ( int a, int b ),没有修改StringBuilder对象? a.append("value")我会修改 StringBuilder 对象的值?