[PATCH] RFR Bug-pending: Enable Hotspot to Track Native Memory Usage for Direct Byte Buffers

Zhengyu Gu zgu at redhat.com
Fri Apr 13 14:32:01 UTC 2018



On 04/13/2018 10:29 AM, Zhengyu Gu wrote:
> Hi Adam,
> 
> On 04/13/2018 10:14 AM, Adam Farley8 wrote:
>> 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.
> 
> This header overhead only incurs when detail native memory tracking is on.
Oops, should say "native memory tracking is on"

-Zhengyu

> 
>>
>> 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):
> 
> I don't understand why you care about this header? The overheads are not 
> counted to allocators or memory categories (mtOther in this case), but 
> reported by NMT as tracking overhead.
> 
> Thanks,
> 
> -Zhengyu
> 
>>
>> 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 hotspot-dev mailing list