Core Java

Default HotSpot Maximum Direct Memory Size

In my previous blog post Improved Documentation of HotSpot Options in Java 8, I wrote about the misunderstandings surrounding the HotSpot JVM‘s default setting for non-standard option -XX:MaxDirectMemorySize. In this post, I look at a simple way to determine the “default” maximum direct memory size in the HotSpot JVM.

The Java 8 documentation for the Java launcher states the following regarding -XX:MaxDirectMemorySize (I added the emphasis):

Sets the maximum total size (in bytes) of the New I/O (the java.nio package) direct-buffer allocations. Append the letter k orK  to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes. By default, the size is set to 0, meaning that the JVM chooses the size for NIO direct-buffer allocations automatically.

The above explains that 0 is the default for maximum direct memory size in HotSpot when no size is explicitly specified via the -XX:MaxDirectMemorySize option. Using options such as -XX:+PrintFlagsInitial and -XX:+PrintFlagsFinal doesn’t help in this case because the values these would display is also zero when not explicitly specified. For example, running java -XX:+PrintFlagsFinal -version displays:

1
size_t MaxDirectMemorySize                       = 0

As far as I know, there is no “standard” way to access the maximum direct memory size. The class java.lang.Runtime provides information on approximate free memory in the JVM, total memory in the JVM, and maximum memory the JVM will attempt to use. Although java.lang.management.MemoryMXBean offers non-heap memory usage in addition to heap memory usage, this non-heap usage refers to the “method area” and possibly an implementation’s “internal processing or optimization” rather than to direct memory.

There are some non-standard approaches to determining one’s HotSpot JVM default maximum memory size. In the StackOverflow thread Is there a way to measure direct memory usage in Java?, whiskeyspider writes about sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPool().getMemoryUsed()‌​ and sun.misc.VM.maxDirectMemory(). These HotSpot-specific classes respectively indicate the amount of direct memory being used and the maximum amount of direct memory that can be used.

The sun.misc.SharedSecrets class provides information on direct memory use via method calls getJavaNioAccess().getDirectBufferPool() to access an instance of sun.misc.JavaNioAccess.BufferPool. The BufferPool interface defines three methods providing direct memory related details: getCount(), getTotalCapacity(), and getMemoryUsed(). Although these methods provide interesting details about direct memory use, they don’t tell us what the maximum direct memory is.

The sun.misc.VM.maxDirectMemory() method in the HotSpot JVM supplies us with the maximum direct memory whether it was explicitly specified with -XX:MaxDirectMemorySize= or whether it was implicitly set such that -XX:MaxDirectMemorySize=0 (default) and the VM selects the maximum size of direct memory.

To help demonstrate using these methods to determine maximum direct memory and direct memory used, I first introduce a utility I’ll be using in my examples. This enum is named MemoryUnit and is adapted for this post from dustin.utilities.memory.MemoryUnit.java. I could have used Apache Commons‘s FileUtils.byteCountToDisplaySize(long) or Brice McIver’s more elaborate adaptation of it, but decided to use this simple TimeUnitinspired enum as shown next.

MemoryUnit.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package dustin.examples.maxdirectmemory;
 
/**
 * Representation of basic memory units.
 */
public enum MemoryUnit
{
   /** Smallest memory unit. */
   BYTES,
   /** "One thousand" (1024) bytes. */
   KILOBYTES,
   /** "One million" (1024x1024) bytes. */
   MEGABYTES,
   /** "One billion" (1024x1024x1024) bytes. */
   GIGABYTES;
 
   /** Number of bytes in a kilobyte. */
   private final double BYTES_PER_KILOBYTE = 1024.0;
   /** Number of kilobytes in a megabyte. */
   private final double KILOBYTES_PER_MEGABYTE = 1024.0;
   /** Number of megabytes per gigabyte. */
   private final double MEGABYTES_PER_GIGABYTE = 1024.0;
 
   /**
    * Returns the number of bytes corresponding to the
    * provided input for a particular unit of memory.
    *
    * @param input Number of units of memory.
    * @return Number of bytes corresponding to the provided
    *    number of particular memory units.
    */
   public double toBytes(final long input)
   {
      double bytes;
      switch (this)
      {
         case BYTES:
            bytes = input;
            break;
         case KILOBYTES:
            bytes = input * BYTES_PER_KILOBYTE;
            break;
         case MEGABYTES:
            bytes = input * BYTES_PER_KILOBYTE * KILOBYTES_PER_MEGABYTE;
            break;
         case GIGABYTES:
            bytes = input * BYTES_PER_KILOBYTE * KILOBYTES_PER_MEGABYTE * MEGABYTES_PER_GIGABYTE;
            break;
         default :
            throw new RuntimeException("No value '" + this + "' recognized for enum MemoryUnit.");
      }
      return bytes;
   }
 
   /**
    * Returns the number of kilobytes corresponding to the
    * provided input for a particular unit of memory.
    *
    * @param input Number of units of memory.
    * @return Number of kilobytes corresponding to the provided
    *    number of particular memory units.
    */
   public double toKiloBytes(final long input)
   {
      double kilobytes;
      switch (this)
      {
         case BYTES:
            kilobytes = input / BYTES_PER_KILOBYTE;
            break;
         case KILOBYTES:
            kilobytes = input;
            break;
         case MEGABYTES:
            kilobytes = input * KILOBYTES_PER_MEGABYTE;
            break;
         case GIGABYTES:
            kilobytes = input * KILOBYTES_PER_MEGABYTE * MEGABYTES_PER_GIGABYTE;
            break;
         default:
            throw new RuntimeException("No value '" + this + "' recognized for enum MemoryUnit.");
      }
      return kilobytes;
   }
 
   /**
    * Returns the number of megabytes corresponding to the
    * provided input for a particular unit of memory.
    *
    * @param input Number of units of memory.
    * @return Number of megabytes corresponding to the provided
    *    number of particular memory units.
    */
   public double toMegaBytes(final long input)
   {
      double megabytes;
      switch (this)
      {
         case BYTES:
            megabytes = input / BYTES_PER_KILOBYTE / KILOBYTES_PER_MEGABYTE;
            break;
         case KILOBYTES:
            megabytes = input / KILOBYTES_PER_MEGABYTE;
            break;
         case MEGABYTES:
            megabytes = input;
            break;
         case GIGABYTES:
            megabytes = input * MEGABYTES_PER_GIGABYTE;
            break;
         default:
            throw new RuntimeException("No value '" + this + "' recognized for enum MemoryUnit.");
      }
      return megabytes;
   }
 
   /**
    * Returns the number of gigabytes corresponding to the
    * provided input for a particular unit of memory.
    *
    * @param input Number of units of memory.
    * @return Number of gigabytes corresponding to the provided
    *    number of particular memory units.
    */
   public double toGigaBytes(final long input)
   {
      double gigabytes;
      switch (this)
      {
         case BYTES:
            gigabytes = input / BYTES_PER_KILOBYTE / KILOBYTES_PER_MEGABYTE / MEGABYTES_PER_GIGABYTE;
            break;
         case KILOBYTES:
            gigabytes = input / KILOBYTES_PER_MEGABYTE / MEGABYTES_PER_GIGABYTE;
            break;
         case MEGABYTES:
            gigabytes = input / MEGABYTES_PER_GIGABYTE;
            break;
         case GIGABYTES:
            gigabytes = input;
            break;
         default:
            throw new RuntimeException("No value '" + this + "' recognized for enum MemoryUnit.");
      }
      return gigabytes;
   }
}

With MemoryUnit available as a helper utility, the next code example demonstrates using the methods on the JavaNioAccess.BufferPool provided by SharedSecrets. These values aren’t the maximum possible direct memory, but are instead estimates of the direct memory already being used.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/**
 * Write amount of direct memory used to standard output
 * using SharedSecrets, JavaNetAccess, the direct Buffer Pool,
 * and methods getMemoryUsed() and getTotalCapacity().
 */
public static void writeUsedDirectMemoryToStdOut()
{
   final double sharedSecretsMemoryUsed =
      MemoryUnit.BYTES.toMegaBytes(
         SharedSecrets.getJavaNioAccess().getDirectBufferPool().getMemoryUsed());
   out.println(
      "sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPool().getMemoryUsed(): "
         + sharedSecretsMemoryUsed + " MB");
   final double sharedSecretsTotalCapacity =
      MemoryUnit.BYTES.toMegaBytes(SharedSecrets.getJavaNioAccess().getDirectBufferPool().getTotalCapacity());
   out.println("sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPool().getTotalCapacity(): "
      + sharedSecretsTotalCapacity + " MB");
}

The above code can be executed after placing something in direct memory with a line similar to the following:

1
final ByteBuffer bytes = ByteBuffer.allocateDirect(1_000_000);

When direct memory is used as shown above and the code above that is executed, the output looks like this:

1
2
sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPool().getMemoryUsed(): 0.95367431640625 MB
sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPool().getTotalCapacity(): 0.95367431640625 MB

The methods just demonstrated provide estimates of the amount of direct memory being used, but still do not show the maximum available direct memory. This can be determined with VM.maxDirectMemory as shown in the next code listing.

01
02
03
04
05
06
07
08
09
10
11
12
/**
 * Write maximum direct memory size set (explicitly or
 * implicitly) for this VM instance using VM's
 * method maxDirectMemory().
 */
public static void writeMaximumDirectMemorySizeToStdOut()
{
   final double vmSize =
      MemoryUnit.BYTES.toMegaBytes(VM.maxDirectMemory());
   out.println(
       "sun.misc.VM.maxDirectMemory(): " + vmSize + " MB");
}

When the above code is executed on my laptop with JDK 8 and no explicitly specified -XX:MaxDirectMemorySize, the result looks like this:

1
sun.misc.VM.maxDirectMemory(): 1804.5 MB

From this, I can see that the JVM running on my machine has a default maximum direct memory size of approximately 1.8 GB. I know this is the default because I haven’t explicitly specified -XX:MaxDirectMemorySize on the command-line and because running the sample Java application with -XX:+PrintFlagsFinal shows zero (default) for it.

To assure myself that this approach is showing the correct maximum direct memory, I can explicitly specify the maximum direct memory on the command line and see what the code shown above writes out. In this case, I’m providing -XX:MaxDirectMemorySize=3G on the command-line. Here’s the output when I run the above code with that explicit setting:

1
sun.misc.VM.maxDirectMemory(): 3072.0 MB

Conclusion

When one needs to know the maximum direct memory available for a particular application running on the HotSpot JVM, the method VM.maxDirectMemory() is what is probably the easiest way to get this information if -XX:MaxDirectMemorySize is not explicitly specified. Knowing the maximum allowed direct memory can be useful when working directly with Java NIO or even when working with Java NIO indirectly while working with products that use Java NIO such as Terracotta and Hazelcast “offheap” options.

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button