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.