即使调用System.gc(),RAM也不会清除

时间:2019-06-07 02:22:50

标签: java heap-memory

这是我的主要应用程序问题的示例代码。当我生成数据时,它继续占用RAM(顺便说一句)。但是当我停止该进程时,它仍然保留在RAM中(我可以在任务管理器中看到它)。我尝试使用System.gc(),但是也没有用。由于占用更多内存,程序有时会卡住。希望有人能帮助我。

public static ArrayList<String> my = new ArrayList<>();
public static int val = 0;
// Code for Start Button 
try {
    new Thread(new Runnable() {
        @Override
        public void run() {
            String ss = "";
            for (int i = 0; i < 10000; i++) {
                ss += "ABC";
            }
            while (true) {
                if (val == 0) {
                    for (int i = 0; i < 30; i++) {
                        my.add(ss + new SimpleDateFormat("yyyyMMddHHmmssSSS"));
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ex) {
                    }
                } else {
                    Thread.yield();
                    break;
                }
            }
        }
    }).start();
} catch (Exception e) {
    e.printStackTrace();
}
// Code for Stop Button
val = 1;
my.clear();
my = null;
System.gc();
Runtime.getRuntime().freeMemory();

1 个答案:

答案 0 :(得分:2)

垃圾收集取决于各种因素,例如您使用的是哪个收集器,计算机的物理内存以及您使用的JVM版本。由于您在这里没有太多提及它们,因此很难预测这可能是造成这种情况的原因。我认为您正在使用Java 8,因为它是当今最受欢迎的版本。

自Java 8开始,JVM内存模型发生了变化。也就是说,现在没有Permanent Generation空间。这是String Pool所在的空间(我要使用String,因此您在循环中使用String串联)。请参阅此Java (JVM) Memory Model – Memory Management in Java文档。自您声明类/静态引用以来,Permanent Generation就一直存在。

从Java 8开始,取代了Permanent Generation,有了一个名为Metaspace的新内存位置,该位置位于JVM之外的主内存中。

此外,当像这样串联String对象时,它不会修改现有对象,因为String是不可变的类型。而是使用新值创建新的String对象,并将它们放入Metaspace中。这可能是您看到内存使用量增加的原因。

即使Metaspace位于主内存/物理内存中并且可以动态扩展,但是仍然存在物理内存限制。这就是为什么我将早期机器的物理内存作为依赖性因素的原因。

当我们进行垃圾收集时,您没有提到任何GC配置。因此,我假设您正在使用并行GC ,它是Java 8的默认收集器(您可以从上面提供的同一链接中找到有关GC的更多信息)。我猜想Parallel GC的性能足以胜任此任务。因此,在没有任何JVM标志的情况下调用System.gc()就足够了。

但是,正如您提到的System.gc()不会清理内存可能会发生,因此您正在使用单独的线程来连接这些字符串。

通常,使用String文字(String s = "abc")创建的String不会成为垃圾回收对象。这是因为在使用文字的每种方法的代码中都有对String对象的隐式引用(请检查答案When will a string be garbage collected in java)。因此,您必须通过结束函数的执行来释放那些隐式引用。

由于您使用的是新的Thread来进行此串联,因此我找不到任何中断线程的地方,并且正在调用Thread.yield()Thread.yield)要通知线程调度程序抓住该特定线程的CPU使用率并标记该线程愿意尽快进行调度,请非常清楚此Thread对象仍然存在,并引用那些String对象,使它们符合垃圾标准。这可能是System.gc()调用无法正常工作的原因。

作为解决方案,请尝试interrupt而不是yield线程。

更新1:

在Java 7之前,String Pool位于PermGen中,不能进行垃圾收集。 PermGen的大小固定,无法在运行时扩展。如果PermGen没有足够的空间,则会出现java.lang.OutOfMemoryError: PermGen错误。作为临时补救措施,我们可以使用PermGen标志来增加-XX:MaxPermSize=512m的大小。

但是请记住,这仅适用于Java 8之前的JVM和Java 7,这在增加String Pool大小可用性方面没有任何区别,因此从Java 7起,String PoolHeap空间。