Thomas Stüfe
2018-11-24 15:41:18 UTC
Hi Peter,
(adding serviceability)
A jmethodid is a pointer to malloc'ed memory.
In more detail, jmethodid points to a table slot containing pointers
to Method* objects, and that table itself lives in C heap. See class
JNIMethodBlock, JNIMethodBlockNode:
http://hg.openjdk.java.net/jdk/jdk/file/30a02b4e6c06/src/hotspot/share/oops/method.cpp#l1928
If I understand the NetBeans profiler coding right, they assume the
jmethodIDs to cluster in up to four clusters of relative vicinity,
with each cluster spanning 30 bits? That is quite an assumption to
make.
This may work more or less accidentally, since malloc'ed pointers are
also clustered naturally. A Clib implementation may allocate only in
the traditional data segment (below sbrk) - I believe AIX and Solaris
do this - which means all malloced pointers are as close to each other
as the data segment is large. But C-Libraries often allocate C-Heap
memory in separate mmape'd blocks (e.g. glibc arenas), and there is no
guarantee where those blocks are. You may still often get lucky - if
all jmethod id block allocations happen in a short timeframe, or if
you just have not many users of C-Heap in the process.
But it is not guaranteed to work. I would probably rather use a
hashmap or similar.
Side note: I believe (and someone please correct me if I am wrong)
that jmethodid used to live in the PermGen, in the Java Heap, and that
only since the PermGen removal in JDK8 they live in C-Heap. If this is
true, this 4x30bit assumption may actually have worked before jdk8,
since the java heap is allocated as one continuous space, with the
PermGen clustered in one part of it.
Best Regards, Thomas
(adding serviceability)
A jmethodid is a pointer to malloc'ed memory.
In more detail, jmethodid points to a table slot containing pointers
to Method* objects, and that table itself lives in C heap. See class
JNIMethodBlock, JNIMethodBlockNode:
http://hg.openjdk.java.net/jdk/jdk/file/30a02b4e6c06/src/hotspot/share/oops/method.cpp#l1928
If I understand the NetBeans profiler coding right, they assume the
jmethodIDs to cluster in up to four clusters of relative vicinity,
with each cluster spanning 30 bits? That is quite an assumption to
make.
This may work more or less accidentally, since malloc'ed pointers are
also clustered naturally. A Clib implementation may allocate only in
the traditional data segment (below sbrk) - I believe AIX and Solaris
do this - which means all malloced pointers are as close to each other
as the data segment is large. But C-Libraries often allocate C-Heap
memory in separate mmape'd blocks (e.g. glibc arenas), and there is no
guarantee where those blocks are. You may still often get lucky - if
all jmethod id block allocations happen in a short timeframe, or if
you just have not many users of C-Heap in the process.
But it is not guaranteed to work. I would probably rather use a
hashmap or similar.
Side note: I believe (and someone please correct me if I am wrong)
that jmethodid used to live in the PermGen, in the Java Heap, and that
only since the PermGen removal in JDK8 they live in C-Heap. If this is
true, this 4x30bit assumption may actually have worked before jdk8,
since the java heap is allocated as one continuous space, with the
PermGen clustered in one part of it.
Best Regards, Thomas
I have recently been looking at some native code that's part of the
NetBeans profiler. This code was crashing for me because it was based
on incorrect assumptions about the sizes of various primitive types.
There is some code which is intended to map jmethodid values to jint
values (see below). This is trivial on 32 bit platforms because the
are the same size. However on 64-bit platforms this is not the case
because jmethodid is (according to the docs) to be treated as a
pointer to an opaque structure and is 64-bit.
So, the code splits the 64-bit jmethodid into a 30-bit 'offset' part
and a 34-bit 'base' part. It then stores the high part in one of four
slots and returns a 32 bit int which is 2 bits for the slot id and 30
bits from the offset part. For the reverse process, it looks up the
slot id, gets the base address and ORs it with the offset. This seems
to work in practice but it is based on the assumption that all
jmethodids will have one of only four base parts - any more than four
can't be specified by a 2 bit id.
So my question is: is this a sound assumption? The answer needs some
detailed knowledge of the internals of the JRE I suppose, so if this
mailing list is not the right place to ask, what is the right place?
I want to fix my crash in the netbeans profiler but I want to do the
right thing rather than just patching it up (which I have done by
doing casts more carefully)
Thanks for your help.
Peter
https://github.com/apache/incubator-netbeans/blob/5405b8c93eeafdd213cffcc59df002464af8d838/profiler/lib.profiler/native/src-jdk15/Stacks.c#L66-L92
and: https://github.com/apache/incubator-netbeans/blob/5405b8c93eeafdd213cffcc59df002464af8d838/profiler/lib.profiler/native/src-jdk15/Stacks.c#L66-L92
NetBeans profiler. This code was crashing for me because it was based
on incorrect assumptions about the sizes of various primitive types.
There is some code which is intended to map jmethodid values to jint
values (see below). This is trivial on 32 bit platforms because the
are the same size. However on 64-bit platforms this is not the case
because jmethodid is (according to the docs) to be treated as a
pointer to an opaque structure and is 64-bit.
So, the code splits the 64-bit jmethodid into a 30-bit 'offset' part
and a 34-bit 'base' part. It then stores the high part in one of four
slots and returns a 32 bit int which is 2 bits for the slot id and 30
bits from the offset part. For the reverse process, it looks up the
slot id, gets the base address and ORs it with the offset. This seems
to work in practice but it is based on the assumption that all
jmethodids will have one of only four base parts - any more than four
can't be specified by a 2 bit id.
So my question is: is this a sound assumption? The answer needs some
detailed knowledge of the internals of the JRE I suppose, so if this
mailing list is not the right place to ask, what is the right place?
I want to fix my crash in the netbeans profiler but I want to do the
right thing rather than just patching it up (which I have done by
doing casts more carefully)
Thanks for your help.
Peter
https://github.com/apache/incubator-netbeans/blob/5405b8c93eeafdd213cffcc59df002464af8d838/profiler/lib.profiler/native/src-jdk15/Stacks.c#L66-L92
and: https://github.com/apache/incubator-netbeans/blob/5405b8c93eeafdd213cffcc59df002464af8d838/profiler/lib.profiler/native/src-jdk15/Stacks.c#L66-L92