Java thread dynamic memory allocation

Andrew Haley aph at redhat.com
Sat Sep 12 10:30:32 UTC 2020


Hi, and welcome.

On 12/09/2020 09:46, Manavjeet Singh wrote:
> I am currently studying GC and java memory model. From the code, I
> understand that GC provides a TLAB to each thread for unsynchronized
> allocation of the non-humongous objects. But I am not able to
> understand what happens when a "new" operator is called in java
> code. Is the function that tackles this "new" operator defined in
> JavaThread class or some other routine is called.

It's an intrinsic defined in the virtual machine.

> Can you please correct me if I am assuming something wrong, and point me to
> relevant functions so that I can understand better.

OK, here goes.

First you'll need to find thread.hpp in the OpenJDK source code. Look
for this:

  ThreadLocalAllocBuffer _tlab;                 // Thread-local eden
  jlong _allocated_bytes;                       // Cumulative number of bytes allocated on
                                                // the Java heap

If you look around for uses of JavaThread::_tlab you'll see what
happens.

To see how it really works you'll need a disassembly of the code
HotSpot generates. I'm using Intel x86 here because I guess that's
what you have.

Here's my test case:

public class Alloc {

    volatile byte[] bytes;

    void allocate() {
        bytes = new byte[8];
    }

    public static void main(String[] args) {
        long count = Long.parseLong(args[0]);
        Alloc a = new Alloc();
        for (long i = 0; i < count; i++) {
            a.allocate();
        }
    }
}

Now to disassemble it. You'll need to build hsdis-amd64.so; that's in
jdk/src/utils/hsdis and the instructions for building it are there.

Create a file called .hotspot_compiler containing this:

dontinline Alloc::allocate
print Alloc::allocate

And run java:

java -XX:CompileCommandFile=.hotspot_compiler Alloc 200000000

You'll see the disassembled code for Alloc::allocate

Here's the code that allocates and initializes the object array bytes[],
with my commentary:


The pointer to the current JavaThread is always in r15.

Load _tlab pointer into rbx:

  0x00007fae98c2820f:   mov    rbx,QWORD PTR [r15+0x140]

Add 0x18 (the to total size of the new object):

  0x00007fae98c28216:   mov    r10,rbx
  0x00007fae98c28219:   add    r10,0x18

Make sure we have enough space in our TLAB. If we haven't we'll
have to create a new TLAB; the code to do  that is elsewhere.

  0x00007fae98c2821d:   cmp    r10,QWORD PTR [r15+0x150]
  0x00007fae98c28224:   jae    0x00007fae98c28396
 ;; B2: #	out( B3 ) <- in( B1 )  Freq: 0.9999

OK, so we do have enough space. Let's create our object. First, update
_tlab to point to the end of our new object. After this we've allocated the
space and we have a pointer to it in rbx.

  0x00007fae98c2822a:   mov    QWORD PTR [r15+0x140],r10

The prefetch instructions are just a hint to the processor to load the
next few bytes in the TLAB into the processor cache ready for the next
TLAB allocation:

  0x00007fae98c28231:   prefetchnta BYTE PTR [r10+0x100]

Initialize the first qword of the object (the lock word):

  0x00007fae98c28239:   mov    QWORD PTR [rbx],0x1
  0x00007fae98c28240:   prefetchnta BYTE PTR [r10+0x140]

The next two dwords are a pointer to the class and the length of
the array:

  0x00007fae98c28248:   mov    DWORD PTR [rbx+0x8],0x860    ;   {metadata({type array byte})}
  0x00007fae98c2824f:   prefetchnta BYTE PTR [r10+0x180]

Initialize the length field, which is 8:

  0x00007fae98c28257:   mov    DWORD PTR [rbx+0xc],0x8

Now write 8 bytes of zeroes to initialize the object: we just happen
to know that r12 contains 0 at this point.

  0x00007fae98c2825e:   mov    QWORD PTR [rbx+0x10],r12

All clear?

-- 
Andrew Haley  (he/him)
Java Platform Lead Engineer
Red Hat UK Ltd. <https://www.redhat.com>
https://keybase.io/andrewhaley
EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671




More information about the hotspot-gc-dev mailing list