6

I have a tiny java console application which I would like to optimize in terms of memory usage. It is being run with Xmx set to only 64MB. The overall memory usage of the process according to different monitoring tools (htop, ps, pmap, Dynatrace) shows values above 250MB. I run it mostly on Ubuntu 18 (tested on other OS-es as well).

I've used -XX:NativeMemoryTracking java param and Native Memory Tracking with jcmd to find out why so much more memory is used outside of the heap.

The values displayed by NMT when summarized were more or less the same as the ones shown by htop as Resident Memory.

NMT:

Total: reserved=1518873KB, committed=255877KB

htop:

enter image description here

I've used several JVM parameters to reduce native memory consumption (reduced stack size, changed GC to serial, Class Data Sharing etc). Both reserved and committed memory metrics went down according to NMT (both malloced and mmaped) by let's say ~50MB in total.

NMT:

Total: reserved=1475110KB, committed=209218KB

All the tools that I'm using (htop, ps, pmap, Dynatrace) show no difference at all. Total memory used by the process is still 250MB.

  1. The question is, why is that? Why reducing native memory usage by JVM does not make any effect on the resident memory used by the java process? Is it reserved somehow upfront and not released?
  2. Is there any other way to effectively reduce memory consumption by the whole java process (outside of the heap which is already optimized and set to only 64MB)?
KnotGillCup
  • 177
  • 1
  • 9
  • Looks like this will be useful to you https://stackoverflow.com/questions/6785754/jvm-process-vs-jvm-heap-memory-usage?rq=1 – codinnvrends Jun 29 '20 at 09:34
  • Thanks @codinnvrends but I still don't understand. I know that JVM uses memory beside the heap. I can see it with full detail with NMT and there I'm seeking for optimization. What I still don't get is why even though NMT shows less memory consumption there is no difference on OS level – KnotGillCup Jun 29 '20 at 20:49

2 Answers2

7

NativeMemoryTracking may report less committed memory than the actual Resident Set Size (RSS) of a process for multiple reasons.

  • NMT counts only certain JVM structures. It does not count memory mapped files (including loaded .jar files), nor memory allocated by libraries other than libjvm. Even native memory allocated by the standard class library (i.e. libjava) is not shown in NMT report.

  • When something allocates memory with a standard system allocator (malloc) and then releases it, this memory isn't always returned back to the OS. The system allocator may keep released memory in a pool for future reuse, but from the OS perspective this memory is considered used (and thus included in RSS).

This answer and this video may give an idea what else takes memory, and how to analyze footprint of a Java process.

This post describes some ideas (both reasonable and extreme) on reducing footprint.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • Thanks @apangin for the detailed answer and for the links. I've already read the post by Aleksey Shipilёv before - it is brilliant. I understand that committed memory shown by NMT reports less than the actual RSS of a process. I would still expect to see at least some improvement. I assume it must be the case that even though NMT shows 50MB less then without JVM tweaks, more memory had been allocated and is now not returned back to OS – KnotGillCup Jul 01 '20 at 20:34
  • 1
    @KnotGillCup Another possible reason is that not all "committed" memory shown by NMT actually adds to the process footprint. E.g. when you decrease Java stack size, NMT will report the corresponding decrease in committed memory, but this will not affect RSS. See [JDK-8191369](https://bugs.openjdk.java.net/browse/JDK-8191369) for details. – apangin Jul 01 '20 at 20:47
  • That is surely the case. One of the things that I've done was reducing the stack size. Great video by the way. Really liked it. – KnotGillCup Jul 01 '20 at 21:17
1
  A typical memory representation of C program consists of following sections.

  1. Text segment
  2. Initialized data segment
  3. Uninitialized data segment
  4. Stack
  5. Heap
  • JVM memory includes: JVM heap, JVM Stack, JVM native stack, The pc Register , and so on.

(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5)

and the JVM heap just a part of Java Heap section (in c program memory representation).

zeno
  • 11
  • 3
  • Thanks, @SvenAugustus but that does not answer any of my questions. I've reduced JVM stack size, memory malloced by GC and so on but the overall process memory is the same as when the process was run with default JVM options.. – KnotGillCup Jun 30 '20 at 06:10