Encourage the JVM to GC rather than grow the heap?

18,492

Solution 1

You could try specifying -XX:MinHeapFreeRatio and -XX:MaxHeapFreeRatio to control heap expansion and shrinking:

  • -XX:MinHeapFreeRatio - when the percentage of free space in a generation falls below this value the generation will be expanded to meet this percentage. Default is 40.
  • -XX:MaxHeapFreeRatio - when the percentage of free space in a generation exceeded this value the generation will shrink to meet this value. Default is 70.

You might also want to experiment with concurrent GC by specifying -XX:+UseConcMarkSweepGC. Depending on your application it could keep the heap size lower at the cost of additional CPU cycles.

The JVM is otherwise going to use the memory you specify as available when it's optimal do to so. You could specify a lower amount like -Xmx768m to keep it contained and it might run fine, although you would increase your risk of running out of memory in a heavy load scenario. Really the only way to use less memory overall is to write code that uses less memory :)

Solution 2

Update Java 15 added two more collectors. Some of the relevant options in ZGC are:

  • -XX:+UseZGC enable ZGC
  • -XX:+ZProactive and -XX:+ZUncommit Both are enabled by default. ZGC will proactively try to both free garbage objects and to release the freed memory to the OS. The rest of the options tweak how aggressively it's done.
  • -XX:ZCollectionInterval=seconds How often the GC runs. Set to a few seconds to ensure that garbage is cleaned up as quickly as possible. (By default it's 0, ie no guarantee the GC will run.)
  • -XX:ZUncommitDelay=seconds Sets the amount of time (in seconds) that heap memory must have been unused before being uncommitted. By default, this option is set to 300 (5 minutes). Set a lower value to get back the memory more quickly
  • -XX:SoftMaxHeapSize The GC will fire more often whenever the application uses more memory than this limit. (This is probably the answer the OP is looking for.)
  • -XX:SoftRefLRUPolicyMSPerMB Not sure how often this option is relevant, but it controls how long some cached objects remain in memory.

Original Answer There are four (actually, five) different garbage collectors in HotSpot, and your options are different for each one.

  • The serial collector has -XX:MinHeapFreeRatio and -XX:MaxHeapFreeRatio, which let you more or less directly control the behavior. I don't have much experience with the serial collector however.
  • The parallel collector (-XX:+UseParallelOldGC) has -XX:MaxGCPauseMillis=<millis> and -XX:GCTimeRatio=<N>. Setting a lower maximum pause time generally causes the collector to make the heap smaller. However, if the pause time is already small the heap won't become smaller. OTOH, if the max pause time is set too low, the application may spend all of its time collecting. Setting a lower gc time ratio also generally makes the heap smaller. You are telling the gc that you're willing to dedicate more CPU time to collection in return for a smaller heap. However, this is just a hint and may have no effect. In my opinion, the parallel collector is close to untunable for the purpose of minimizing the heap's size. This collector works better (it tunes itself) if you let it run for a while and the application's behavior stays constant.
  • The CMS collector (-XX:+UseConcMarkSweepGC) generally requires a larger heap to accomplish its main goal of low pause time, so I won't discuss it.
  • The new G1 collector (-XX:+UseG1GC) isn't as brain-dead as the older collectors. I find that it chooses a smaller heap size on its own. It has a lot of tuning options, although I've only begun to study them. -XX:InitiatingHeapOccupancyPercent, -XX:G1HeapWastePercent, -XX:G1ReservePercent and -XX:G1MaxNewSizePercent may be of interest.

Take a look at the list of java flags.

Solution 3

You could as usually invoke the System.gc() method before allocating the additional 200MB of objects but this will just be a hint to the JVM that it can or can't follow and in anycase you won't know when this takes place.. as stated inside documentation it is a best-effort request.

By the way take into account that garbage collection is an heavy operation so if there's no specific need to do it, just don't try to force it.

Just to know: if you set -Xmx1G then you are telling to the JVM that 1GB is the maximum space for the heap, and since you are specifying that, I don't see why it should try to keep it low if it knows that 1GB will be still ok (we're in the context of memory-managed languages). If you don't want the heap to increase that much just decrease the maximum value so that it will be forced to do GC before allocating new objects, otherwise why are you telling it to use 1GB?

Solution 4

I'd recommend taking a peek here:

http://www.petefreitag.com/articles/gctuning/

Share:
18,492
Dave
Author by

Dave

Lifelong computer enthusiast turned Software developer. Recently emerged from the command-line C world into web systems under .NET. I'm greatly enjoying the fact I haven't had a single pointer-related error in over a year.

Updated on October 16, 2022

Comments

  • Dave
    Dave over 1 year

    (Note that when I say "JVM", I really mean "Hotspot", and I'm running the latest Java 1.6 update.)

    Example situation:

    My JVM is running with -Xmx set to 1gb. Currently, the heap has 500mb allocated, of which 450mb is used. The program needs to load another 200 mb on the heap. Currently, there is 300mb worth of "collectable" garbage in the heap (we'll assume it's all in the oldest generation.)

    Under normal operation, the JVM will grow the heap to 700 mb or so, and garbage collect when it gets around to it.

    What I would like in that situation is for the JVM to gc first, then allocate the new stuff, so that we end up with the heap size staying at 500mb, and the used heap at 350mb.

    Is there a JVM parameter combo that does that?

  • Sebastian Dusza
    Sebastian Dusza about 13 years
    The concept there is no need to free unused memory is in my opinion the major problem with today's "modern" programming languages. I share Electrons_Ahoy concerns. Computers are no longer getting faster as they used to, unfortunately. We have to write better software !
  • Jack
    Jack about 13 years
    That's the tradeoff if you want to forget about memory allocation, nothing strange neither nothing that forbids you from using C or C++ and manage whatever you want by hand. But if you choose an environment which takes care of behaving in the best way by itself then you shouldn't try to force its behavior. Your argumentation is mostly useless since we're in the context of Java, and noone forces you to use it :) It's like arguing versus someone with a big polluting car, just take your bike and forget about pollution..
  • Steve B.
    Steve B. about 13 years
    This might be useful, in that a full gc will be cheaper when the heap used is smaller. If you could choose between "fullGC now->recover 500M->add 500M of new objects" and "add 500M->trigger an automatic GC->recover 500M" the first would be cheaper. Also, although the docs do explicitly state that System.gc() will be taken as "a suggestion", it does work as if it were written as mandatory in my limited messing with it. Not that it's necessarily a good idea - with a 1G heap a fullgc will cause a noticeable hang - at least a few seconds totally locked up.
  • Jack
    Jack about 13 years
    Yes, System.gc() will mostly work all the times, but it doesn't guarantee it.. so use it but be aware of the fact! That's it, I can confirm that in my limited usage of it, it always did what I was expecting for me too.
  • Sebastian Dusza
    Sebastian Dusza about 13 years
    I simply believe that GC concept should be augmented/modified. Java/.NET and others where created in the world where computers got faster and faster all the time. This is no longer true, the reality is quite different :). I like Java and wish there was something similar to delete operator :)
  • Aleksandr Dubinsky
    Aleksandr Dubinsky almost 11 years
    @Seba It's really about Java being targeted for servers, where there's only one app running and it should take all the memory from the OS and use it as it sees fit. Obviously, on a client the situation is completely different. Alas.
  • Aleksandr Dubinsky
    Aleksandr Dubinsky over 9 years
    -1 These options apply only to the serial garbage collector (ie, the one that no one uses anymore). Also, CMS is generally inferior to G1, although perhaps your answer predates it.
  • Leliel
    Leliel over 8 years
    @AleksandrDubinsky While G1 is a very good collector, on Java 7 it is utterly unsuited for any latency sensitive system which does much class loading and unloading, as it does not collect permgen. Java 8 doesn't have that issue
  • Aleksandr Dubinsky
    Aleksandr Dubinsky over 8 years
    @Leliel I don't think any collector collects permgen on Java 7. Hence the name "permanent generation."
  • Leliel
    Leliel over 8 years
    @AleksandrDubinsky CMS collector can be configured to collect permgen. -XX:+CMSClassUnloadingEnabled
  • Holger
    Holger about 2 years
    @SebastianDusza a delete operator would be everything but fast. It would require the underlying implementation to introduce an entire new data structure, to keep track of what have been deleted. Granted, if the delete operator would only update the “used memory” counter, to make people happy, while leaving the actual work to the GC which will only run when there’s a real need for it, it could help some people.