[PATCH] RFR Bug-pending: Enable Hotspot to Track Native Memory Usage for Direct Byte Buffers
Adam Farley8
adam.farley at uk.ibm.com
Fri Apr 13 14:14:49 UTC 2018
Hi Alan, Peter,
I see that native memory is tracked in java.nio.Bits, but that only
includes what the user thinks they are allocating.
When the VM adds extra memory to the allocation amount this extra bit is
not represented in the Bits total.
A cursory glance shows, minimum, that we round the requested memory
quantity up to the heap word size in
the Unsafe.allocateMemory code, and something to do with nmt_header_size
in os:malloc() (os.cpp) too.
Here's a set of solutions for the problem of "what you see isn't what you
get" for DBBs (3 is my favourite, but I
suspect 2 is the most likely to be accepted):
1) Use the code below to call DBB-specific Unsafe methods, allowing us to
track the quantity of native
memory reserved for DBBs at the point of allocation.
Pros:
- Doesn't require changing the Bits class.
- Allows us to easily devise a list of DBB native memory allocation
locations.
- Fits with existing OpenJ9 code, which tracks DBB memory usage *after*
native code has added to the total
memory requested, resulting in more accurate totals than Bits.
Cons:
- Requires some work on the Hotspot side to make it happen if Hotspot
users want the true benefit.
OR
- Requires a commit that only benefits Java if you're running on the
OpenJ9 VM.
2) Modify the Bits code to interrogate the VM via an extra method in
Unsafe, in order to determine the
true quantity of native memory that is being allocated.
E.g. User requests 10 bits, VM says it needs +2, add 12 to cumulative
total, return "true".
User later says to free 10 bits, VM says it needs +2, so we subtract 12
from the total.
Note: For consistency, the VM and Bits should use the same code when
figuring out how much space
will be added to the request by the VM.
Pros:
- Much smaller change than (1)
- Makes Bits much more accurate.
- Retains the Bits interface, and doesn't require a change to Unsafe.
Cons:
- Requires us to add a native method to Bits, or somewhere visible to
Bits.
3) Modify the Bits code to use an internal array for memory reservations,
returns an index. The user
can use this index to:
- Return the amount of memory they requested.
- Return the amount of memory the VM will actually reserve (Bits will
retrieve this from the VM via a
native method).
- Set the address the VM gave them for this reservation.
- Free the memory reserved.
Note: The existing internal totals, along with Shared Secrets, remain.
Pros:
- *Much* more accurate memory tracking for DBBs.
- Easier debugging for DBB overruns, as we know where all the DBBs are.
- Prevents the user trying to free too much or too little memory for an
allocated DBB.
Cons:
- Many changes to Bits.
- Native code changes.
- Probably best to restructure the memory allocation code to share the
same logic
(abstracting out the size+x gubbins so Bits' new native code logic remains
up to date).
Any thoughts?
- Adam
>> Hi Adam,
>>
>> Did you know that native memory is already tracked on the Java side for
>> direct ByteBuffers? See class java.nio.Bits. Could you make use of it?
>>
> Right, these are the fields that are exposed at runtime via
> BufferPoolMXBean. A SA based tool could read from a core file. I can't
> tell if this is enough for Adam, it may be that the his tool reveals
> more details on the buffers in the pools.
>
> -Alan
P.S. Code:
diff --git
a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
@@ -85,7 +85,7 @@
// Paranoia
return;
}
- UNSAFE.freeMemory(address);
+ UNSAFE.freeDBBMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
@@ -118,7 +118,7 @@
long base = 0;
try {
- base = UNSAFE.allocateMemory(size);
+ base = UNSAFE.allocateDBBMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
@@ -632,6 +632,26 @@
}
/**
+ * Allocates a new block of native memory for DirectByteBuffers, of
the
+ * given size in bytes. The contents of the memory are
uninitialized;
+ * they will generally be garbage. The resulting native pointer will
+ * never be zero, and will be aligned for all value types. Dispose
of
+ * this memory by calling {@link #freeDBBMemory} or resize it with
+ * {@link #reallocateDBBMemory}.
+ *
+ * @throws RuntimeException if the size is negative or too large
+ * for the native size_t type
+ *
+ * @throws OutOfMemoryError if the allocation is refused by the
system
+ *
+ * @see #getByte(long)
+ * @see #putByte(long, byte)
+ */
+ public long allocateDBBMemory(long bytes) {
+ return allocateMemory(bytes);
+ }
+
+ /**
* Resizes a new block of native memory, to the given size in bytes.
The
* contents of the new block past the size of the old block are
* uninitialized; they will generally be garbage. The resulting
native
@@ -687,6 +707,27 @@
}
/**
+ * Resizes a new block of native memory for DirectByteBuffers, to the
+ * given size in bytes. The contents of the new block past the size
of
+ * the old block are uninitialized; they will generally be garbage.
The
+ * resulting native pointer will be zero if and only if the requested
size
+ * is zero. The resulting native pointer will be aligned for all
value
+ * types. Dispose of this memory by calling {@link #freeDBBMemory},
or
+ * resize it with {@link #reallocateDBBMemory}. The address passed
to
+ * this method may be null, in which case an allocation will be
performed.
+ *
+ * @throws RuntimeException if the size is negative or too large
+ * for the native size_t type
+ *
+ * @throws OutOfMemoryError if the allocation is refused by the
system
+ *
+ * @see #allocateDBBMemory
+ */
+ public long reallocateDBBMemory(long address, long bytes) {
+ return reallocateMemory(address, bytes);
+ }
+
+ /**
* Sets all bytes in a given block of memory to a fixed value
* (usually zero).
*
@@ -918,6 +959,17 @@
checkPointer(null, address);
}
+ /**
+ * Disposes of a block of native memory, as obtained from {@link
+ * #allocateDBBMemory} or {@link #reallocateDBBMemory}. The address
passed
+ * to this method may be null, in which case no action is taken.
+ *
+ * @see #allocateDBBMemory
+ */
+ public void freeDBBMemory(long address) {
+ freeMemory(address);
+ }
+
/// random queries
/**
Unless stated otherwise above:
IBM United Kingdom Limited - Registered in England and Wales with number
741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
More information about the core-libs-dev
mailing list