Java垃圾回收器和字符串操作

4

我有一个应用程序需要进行大量的字符串操作,但是我发现即使没有将任何东西存储在内存中,jvm的内存使用率也非常高。

我制作了一个简单的示例应用程序来演示这个问题: 我有一个简单的JFrame窗口,里面只有一个按钮。

private void buttonDoWorkActionPerformed(java.awt.event.ActionEvent evt) {
    String randomString = "abc test garbagecollector";

    ArrayList<String> results;

    for(int i=0; i<100000; i++)
        results = doNothing(randomString.split(" "));

    results = null;//No effect
    System.gc();//No effect
}

private static ArrayList<String> doNothing(String[] words) {
    ArrayList<String> results = new ArrayList<String>();

    for (String word : words)
        results.add(word);

    return results;
}

如果您运行此示例,则JVM将占用约50Mo的内存,在单击按钮后,它将升至150Mo并永远不会降下来。 编辑:我指的是Windows任务管理器中的“java.exe”进程。
显然,我正在做很多临时字符串副本,但我希望一旦失去引用,它们将被垃圾收集器释放。
编辑:也许与此无关,但我尝试过使用Java 1.6的32位和64位版本。
2个回答

5

您需要澄清您所说的"it"是什么意思,因为可能存在许多不同层次的解释。一些会在垃圾回收(GC)时减少,而其他一些则不会。

如果您正在查看垃圾回收后的内存使用情况,则应该不会增加,如果您看到其他某些内容增加了,那么很可能是最大内存或其他一些数字。

public static long memoryUsed() {
    return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}

public static void main(String... args) {
    String randomString = "abc test garbagecollector";

    System.gc();
    System.out.printf("Memory used before test started %.1f MB%n", memoryUsed() / 1e6);
    for (int t = 0; t < 5; t++) {
        List<String> results = new ArrayList<String>();

        for (int i = 0; i < 100000; i++) {
            for (String word : randomString.split(" "))
                results.add(word);
        }
        long beforeGC = memoryUsed();
        results = null;
        System.gc();
        System.out.printf("%d: before GC %.1f MB used, after GC %.1f MB used%n",
                t, beforeGC / 1e6, memoryUsed() / 1e6);
    }
}

打印
Memory used before test started 5.7 MB
0: before GC 61.8 MB used, after GC 5.8 MB used
1: before GC 44.3 MB used, after GC 5.8 MB used
2: before GC 44.3 MB used, after GC 5.8 MB used
3: before GC 44.3 MB used, after GC 5.8 MB used
4: before GC 44.3 MB used, after GC 5.8 MB used

我只是在Windows任务管理器中查看“java.exe”进程。 - KVM
Java在启动时分配最大堆大小,这使用虚拟内存而非物理内存。 - Peter Lawrey
这里有一些我不理解的地方。无论是物理还是虚拟的,java.exe仍然占用了我不能用于其他应用程序的内存。 - KVM
1
只有你的物理内存是有限制的,但在大多数现代处理器上,你的虚拟内存可以扩展到约256 TB。 - Peter Lawrey
但这仍然没有解决问题:当java.exe超过可用内存时,我会收到内存不足的异常。如果这是“无限”的虚拟内存,为什么会发生这种情况呢? - KVM

0

字符串可能正在进行内部化,这是与Java和字符串相关的一些VM设计决策有关的问题。您可以尝试获取VisualVM或其他框架,连接到您正在运行的JVM并查看对象,或者执行堆转储并查看结果。


这实际上不是问题。但是,一旦不再有对它们的引用,国际化字符串也可以进行垃圾回收。 - Dolda2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接