-1

This is a sample code of my main application problem. When I generate the data it continues to take the RAM (by the way its OK). But when I stop the process it still remain in the RAM (I can see it in the Task Manager). I tried to use the System.gc() but it also didn't work. At some point the program got stuck, because of it taking up more memory. Hope somebody can help me.

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();
user207421
  • 289,834
  • 37
  • 266
  • 440
  • Check out [this](https://stackoverflow.com/a/66573/10303335) asnwer. Seems to me that it covers your question mostly. – ymdred16 Jun 07 '19 at 02:26
  • 2
    There is no Swing mentioned anywhere in the uncompilable code snippet. Why is that tag added? Try and reproduce it without any Swing classes. If it depends on the Swing API, post a [mre] of that. BTW - The JVM will do garbage collection when it feels it is necessary. Unless the code is throwing `OutOfMemoryError` I doubt it is necessary. – Andrew Thompson Jun 07 '19 at 02:37
  • 3
    *"when I stop the process"* How can you run `gc()` if you've stopped the Java process? How can you see it in the Task Manager if you've stopped the Java process? – Andreas Jun 07 '19 at 03:02
  • @Andreas: I don't think OP really means the process, I think they mean their 'Stop Button' which sets a shared field which _should_ cause the `while` loop in the `run` method to `break` -- although since it isn't `volatile` or used in `synchronized` the Java memory model doesn't promise this will work. OP: garbage collection doesn't normally return memory to the OS, only makes it available for use by new allocations within the same JVM process. – dave_thompson_085 Jun 07 '19 at 05:05
  • @dave_thompson_085 I don't think so either, but that is what OP said, so if OP meant something else, then OP needs to edit the question and clarify it. – Andreas Jun 07 '19 at 17:10

1 Answers1

2

Garbage collection depends on the various factor like which collector you're using, machine's physical memory as well as the JVM version you are using. Since you're not mentioned so much here about them, bit hard to predict which could be the cause for this. I assume you're using Java 8 since that's the more popular version nowadays.

Since Java 8, There's a change in JVM memory model. Which is, now there is no Permanent Generation space. This's the space where String Pool located (I'm going with String hence you're using a String concatenation in loops). Refer this Java (JVM) Memory Model – Memory Management in Java document. Permanent Generation is where the class/static references live since you declare them as well.

Instead of Permanent Generation, Since Java 8, there's a new memory location called Metaspace which lives in the Main memory outside of JVM.

Also, when you concatenate String objects like this, it won't modify the existing object as String is immutable type. Instead, it creates new String objects with the new value and put them into the Metaspace. This might be the reason you're seeing a memory usage increment.

Even though Metaspace is located inside the main memory/physical memory and it can dynamically expand, still has the physical memory limitation. That's why I told earlier machine's physical memory as a dependency factor.

When we come to garbage collection, you haven't mention any GC config. So I assume you are using Parallel GC which is the default collector of Java 8 (You can find more about the GCs from same link provided above). I guess the Parallel GC's performance is adequate for this task. Therefore invoking System.gc() whould be enough without any JVM flag.

But, as you mentioned that System.gc() doesn't clean up the memory could occurs hence you're using a separate thread to concatenate these Strings.

Usually, Strings that created using String literal (String s = "abc") would not become garbage eligible. This because there is a implicit reference to the String object in the code of every method that uses the literal (Check this answer When will a string be garbage collected in java). Thus, you have to loose those implicit references by ending the execution of the function.

Since you're using a new Thread to do this concatenation and I can't find any place where you interrupt the thread and you're invoking the Thread.yield()(Thread.yield) to inform the thread scheduler to seize the usage of the CPU for this particular thread and mark the thread is willing to be scheduled as soon as possible again, make pretty much clear this Thread object still lives and refers those String objects not making them garbage eligible. This maybe the reason System.gc() invocation is not working.

As a solution, try to interrupt the thread instead of yield.

Update 1:

Before Java 7, String Pool was located in PermGen, which is not eligible for garbage collection. PermGen has a fixed size and not capable to expand at runtime. If PermGen has not enough space, it gives java.lang.OutOfMemoryError: PermGen error. As a temporary remediation we can increase the PermGen size using -XX:MaxPermSize=512m flag.

But remember this is only works on JVMs before Java 8 and in Java 7, this doesn't make any different in the sense of increasing String Pool size availability hence Java 7 onwards, String Pool has moved to Heap space.

Sachith Dickwella
  • 1,152
  • 2
  • 11
  • 18
  • 1
    `String`s weren't in PermGen before and aren't in Metaspace now. Note the `Thread.yield()` is followed by `break` which causes the Thread function to return and therefore the Thread exits -- if the `val=1` gets seen at all by this Thread. – dave_thompson_085 Jun 07 '19 at 05:13
  • @dave_thompson_085, I guess, `String Pool` was in `PermGen` (Before Java 7) and after moved to `Heap` space. So, afterwards it should be in `Heap` space, Not in `Meatspace`. My bad. – Sachith Dickwella Jun 07 '19 at 06:13
  • @SachithDickwella Thank you very much for your explained answer. As Im new to java it really helped me a lot to understand my error – Hashain Lakshan Jun 07 '19 at 14:20