2

We have a simple microservice setup, based on Spring Boot and Java 8 on Windows servers.

Many of the services have a low load, since they serve as integrations to all kinds of external partners. So they are idle a lot of the time.

The problem is that the JVM only releases memory back to the OS, when a garbage collection is triggered. So a service might start using 32mb, then serve a single request and allocate 2GB of memory. If there is no other activity on that service, it will not GC and other services on the server will suffer.

Triggering a GC externally or internally with a System.gc works just fine and I have figured out how to use -XX:MaxHeapFreeRatio and -XX:MinHeapFreeRatio with -XX:+UseG1GC to control when the heap should expand and release memory to the OS.

My question is: What is the best way to ensure that memory is relased back to the OS when the JVM is idle?

One idea would be to have the service monitor itself and trigger a System.gc efter a period of idleness, but that might be tricky and errorprone. So hoping for better suggestions.

You can reproduce by running X instances of this program. About 10 made my Windows machine with 8GB give up.

import java.util.*;

public class Load {
  public static void main(String[] args) throws Exception {
    alloc();
    Scanner s = new Scanner(System.in);
    System.out.println("enter to gc ");
    s.nextLine();
    System.gc();
    System.out.println("enter to exit");
    s.nextLine();
  }

  private static void alloc() {
    ArrayList<String[]> strings = new ArrayList<>();
    int max = 1000000;
    for (int i = 0; i < max; i++) {
      strings.add(new String[500]);
    }
  }
}

c:\> java -server -XX:+UseG1GC -Xms32m -Xmx2048m Load

Edit: This was marked as a duplicate two times, but it is not a duplicate of the linked questions. The first question is a 2010 version of the same question, but that question is on why the GC does not release memory back to the OS (which was not possible at that time). The other question is about basic GC settings, that I already wrote that I understand. I wish a discussion of how to trigger the garbage collector when the system is idle. So running System.gc every five seconds is not acceptable, because that would have a high risk of colliding with valid requests and ruin the response times.

Lasse L
  • 1,117
  • 1
  • 8
  • 10
  • This is probably a duplicate of http://stackoverflow.com/questions/4952568/is-there-a-way-to-lower-java-heap-when-not-in-use also, calling 'System.gc()' doesn't guarantee that GC will be initiated at that point, see http://stackoverflow.com/questions/66540/when-does-system-gc-do-anything – Finbarr O'B Jul 01 '16 at 15:21
  • My question is not a duplicate of that question. I understand the basic mechanics of GC and how heap are gives back to the OS. I also understand the System.gc comes without guarantees, though I have yet to see it ignore a request. I want a discussion of how best to invoke explicit garbage collections, when the system is idle. – Lasse L Jul 01 '16 at 15:31
  • Apologies, I seem to have missed the actual point of your post "My question is: What is the best way to ensure that memory is relased back to the OS when the JVM is idle?" – Finbarr O'B Jul 01 '16 at 15:35
  • Edited so the question is in bold ;-) – Lasse L Jul 01 '16 at 15:39
  • @AndyWilkinson can you unmark this as a dupe. This question is *releated* to releasing memory to the OS, but it has a twist than none of the other questions has. I want to release the memory when my service is *idle*. Plus I already stated in the question that I understand the basics of releasing memory back to the OS. – Lasse L Jul 04 '16 at 07:07
  • 1
    @LasseL Sorry, I'd missed that subtlety. I've re-opened the question. – Andy Wilkinson Jul 04 '16 at 08:56
  • Don’t you have the feeling that running ten services having a peek usage of 2GB with having only 8GB of RAM total, is like playing Russian roulette on your machine? – Holger Jul 04 '16 at 16:05
  • @Holger that was only something I did to break my own development machine to reproduce the problem. I wanted to learn if the JVMs would GC when the physical machine got under pressure. It doesn't. The program I posted requires less than 10mb to run. But it will keep a hold of the temporary 2GB it allocates until you trigger a GC manually. It feels like a mission feature of the JVM to me. – Lasse L Jul 04 '16 at 19:14
  • On my system, memory pressure doesn’t triggers GC, but still, Windows steals the unused memory from the processes to provide it to the one needing it. After all, “allocated memory” is just a number, only closely related to the assignment of physical memory to processes. – Holger Jul 05 '16 at 08:53

1 Answers1

1

If calling System.gc() fulfills your needs, I would recomend to use spring scheduler to run a periodic task every x sedonds.

This is quite easy to implement, some annotations

@EnableAsync
@EnableScheduling
@Scheduled(cron = "...")

is all you need. See spring scheduling for details.

Edit

Calling System.gc() gives only suggests to start the garbage collection, its still up to the JVM to decide when to do it or not.

To find out, if your system is idle or not, you could use the spring metrics. There are some sub classes of

org.springframework.boot.actuate.endpoint.PublicMetrics

like TomcatPublicMetrics or SystemPublicMetrics that give you information about the system. You can get them injected using @Autowire and call mertics() to get single values. Based on that you might be able to decide, if your system is idle or not,

  • Well, yeah. Something alone those lines is what I am thinking. But I am also thinking that a solution like that would also trigger unwanted garbage collections when the system is NOT idle. E.g. trigger a full heap GC in the middle of a request will probably ruin my SLA by lots of long responses caused by badly planned GC. – Lasse L Jul 01 '16 at 19:05
  • Accepting this answer. It just feels like a hack. I would have prefered some external solution, like a clever JVM flag or something else. – Lasse L Jul 04 '16 at 07:10