0

In order to prevent an OutOfMemoryError I would like to create a code that cleans some caches in my program when there is a danger of outgrowing the available RAM.

How I can detect from inside the code when memory is at a certain percentage from the maximum available, and be able to react?

rghome
  • 7,212
  • 8
  • 34
  • 53
Askar Ibragimov
  • 6,494
  • 15
  • 65
  • 132
  • 2
    You can't / shouldn't. Use SoftReference when implementing caches. – Michael Dec 18 '17 at 14:33
  • 1
    What about using soft/weak references in the cache, and the garbage collector automatically clears them if more space is needed? – escitalopram Dec 18 '17 at 14:34
  • Yes, you should **not** use https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#freeMemory-- – Marco13 Dec 18 '17 at 14:38
  • @Michael when telling juniors that they should not do something, please, also tell them why it's a bad idea ;-) – Jörg Dec 18 '17 at 14:46
  • @Marco13 when telling juniors that they should not do something, please, also tell them why it's a bad idea ;-) – Jörg Dec 18 '17 at 14:46
  • 1
    Part of the problem with checking free memory is, that it only tells you a number. You still don't know enough detail how your JVM memory parts are filled (can be very much different, depending on your choice of and settings for garbage collector). So, you don't know where your particular bottleneck really are. Therefore, it's much better to have a cleaner design as recommended by Michael and Marco13. – Jörg Dec 18 '17 at 14:58
  • @Jörg, what if I have a business case that requires me to keep exact amount of cache entries and only if crash due to OOM is looming, degrade gracefully by freeing some entries from cache? The key thing: application must KNOW that it is happened. As a side note, all claims made our of context of business task are usually not so useful. So I'd strongly suggest against "labels" such as "junior" – Askar Ibragimov Dec 18 '17 at 15:10
  • Possible duplicate of [How to do I check CPU and Memory Usage in Java?](https://stackoverflow.com/questions/74674/how-to-do-i-check-cpu-and-memory-usage-in-java) – rghome Dec 18 '17 at 15:11
  • What necessitated the question? Is the app suddenly getting more traffic than expected? Is scaling up with more memory or more cluster nodes now warranted? – Andrew S Dec 18 '17 at 16:00
  • @Jörg you don't need to post the same comment twice – Michael Dec 19 '17 at 09:05
  • 1
    @AskarIbragimov I'm sorry, if you got the impression I'd label you as a junior, that was by no way my intention - I just wanted to ask the commenters, that if they give some rule like advice ("you should not..."), to also share the reasoning behind such a dogma – Jörg Dec 19 '17 at 15:58

2 Answers2

1

Detecting the memory consumption in Java to react on your code on it is not a good idea. You never know how the garbage collector behaves, when it is started and how often, how much memory it can free up, …

You could use a WeakReference (java.lang.ref) if you want to prevent that a reference to an object prevents that it can be removed by the garbage collector. But if you implement a cache, this could make the cache useless because your cached objects might be removed very quickly and to often.

I would propose to use an LRU-Cache. Such a cache has a certain capacity. If this capacity is exceeded, the least recently used elements will kicked out of the cache. This prevents in a simple way, that you cache can grow infinitely.

You can find some simple implementations if you google for it:

public class LRUMap<K, V>  extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 1L;
    private final int capacity;

    public LRUMap(final int capacity) {
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }
}

If you need more I would check for existing cache implementations. They might support additional configuration capabilities like e. G. maximum age for an entry of you cache.

Stefan Großmann
  • 591
  • 5
  • 16
  • we actually have a limited cache, but if memory is low, I can shift that limitation towards less values kept. Just need to find out that memory is actually low. – Askar Ibragimov Dec 18 '17 at 15:07
  • I really doubt if this can be done in a reliable way. You could try it with Runtime.getRuntime().totalMemory() and Runtime.getRuntime().maxMemory() which should indicate if the current memory usage is at its limit. – Stefan Großmann Dec 18 '17 at 15:43
1

It turns out (to my surprise!) that there is a semi-reliable way to detect memory usage crossing preset thresholds.

The MemoryPoolMXBean class provides a way to set usage thresholds on a memory pool, and get a notification when when a pool's usage exceeds the threshold.

You can get hold of the memory MXBean instances for a JVM by calling ManagementFactory.getMemoryPoolMXBeans().

There are two kinds of threshold, and you need to understand the distinction between them to use them correctly. It is complicated: refer to the javadoc.

It should also be noted that the spec:

  • says that not all kinds of space support threshold checking,
  • says there are no constraints on when threshold crossing is tested for:

    "A Java virtual machine performs usage threshold crossing checking on a memory pool basis at its best appropriate time ..."

  • explains how to test if threshold checking is supported, and how to register for notifications.

Stephen C
  • 632,615
  • 86
  • 730
  • 1,096