• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

JNI Book Recommendation

 
Marshal
Posts: 4499
572
VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have started using JNI to communicate with some devices over a I2C bus on a Linux x86 platform. While looking for good reference document, I found The Java Native Interface: Programmer's Guide and Specification - Sheng Liang. It's a great book, and after reading through a lot of it I've have my Java application talking with the I2C devices.

The book is over 15 years old - has anything changed with JNI since it was written? Are there any other book (or other resource) recommendations for JNI?
 
Bartender
Posts: 1210
25
Android Python PHP C++ Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
That book was written in 1999 when Sun JDK1.2 was the latest JDK version.
Since then, below are some interface changes I could find from going through jni.h of different versions:

1.2 -> 1.3:
No changes

1.3 -> 1.4:
This version had the most interface changes so far:
  • Introduced support for creating and querying NIO direct byte buffers - NewDirectByteBuffer, GetDirectBufferAddress and GetDirectBufferCapacity
  • Introduced AttachCurrentThreadAsDaemon to attach a thread as daemon thread, so that JVM exits if it's the only one running


  • 1.4 -> 1.5:
    No major changes. Just made some function arguments 'const's.

    1.5 -> 1.6:
  • Introduced GetObjectRefType to query type of a reference - local or weak or global
  • Removed an outdated struct, JDK1_1InitArgs


  • 1.6 -> 1.7:
    No changes

    1.7 -> 1.8:
    Normally, when there are interface changes, jni.h introduces a new JNI_VERSION_<ver> constant.
    Weirdly, one such new version was introduced in 1.8, but without any actual interface changes, and then removed again in JDK9 .
    Perhaps something was planned, and then dropped.



    --------
    As such, that entire book is still relevant and valid.
    There are also some alternative, less verbose ways to interface with native code such as JNA, Swig and Javacpp that you might want to try out.
     
    Ron McLeod
    Marshal
    Posts: 4499
    572
    VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Karthik - thanks for the summary of changes. It seems like it hasn't really changed too much, and I don't see anthing from the changes you listed that would help with what I am currently doing.

    One place where I was having some problems was using callbacks when there was more than instance of the class which uses JNI in the same thread. I needed to be able to store instance-specific context data, and to do that I needed be to able to identify each instance individually. I ended up calling the instance's hashCode() method to find a unique identifier for the instance. I'm not if there is a better way to do this, but is does work (although it may not be very efficient).

    I agree with you regarding the verbosity of the code. I did take a brief look at JNA, but I didn't find a lot of documentation and other reference material for it (maybe I didn't look in the right places), and since I already had Liang's book, I decided to use JNI.
     
    Marshal
    Posts: 28193
    95
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Ron McLeod wrote:I ended up calling the instance's hashCode() method to find a unique identifier for the instance. I'm not if there is a better way to do this, but is does work (although it may not be very efficient).



    That sounds a bit scary, actually, since hash codes don't have to be unique over all instances of a class. But maybe you're getting away with that for the classes you're working with.
     
    Ron McLeod
    Marshal
    Posts: 4499
    572
    VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Paul Clapham wrote:That sounds a bit scary, actually, since hash codes don't have to be unique over all instances of a class. But maybe you're getting away with that for the classes you're working with.


    I figured in this case it would be safe, since the class does not override Object's hashCode() method, and the API doc says: [hashCode] is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language. Also, the open() implementation in the library limits the number of instances to 10, so even if the hashCode was calculated a different way, the probability of a collision should be pretty small.
     
    Karthik Shiraly
    Bartender
    Posts: 1210
    25
    Android Python PHP C++ Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    jobject is actually a pointer. Why not use jobject itself as the key, instead of hashcode?
     
    Ron McLeod
    Marshal
    Posts: 4499
    572
    VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    That is what I tried first, but I found that the values for jobject obj were the same when multiple instances where instantiated in the same method. If I am doing something wrong, please point it out.


    instance 1: net.starsolutions.gpio.PCA9555Gpio@3e0a765c
    open() obj=0x7f72847248a0, hashCode=0x3e0a765c
    instance 2: net.starsolutions.gpio.PCA9555Gpio@3495868c
    open() obj=0x7f72847248a0, hashCode=0x3495868c


    instance 1: net.starsolutions.gpio.PCA9555Gpio@44b46c4c
    open() obj=0x7f8fbb9a5898, hashCode=0x44b46c4c
    instance 2: net.starsolutions.gpio.PCA9555Gpio@6c2fdbb1
    open() obj=0x7f8fbb9a5890, hashCode=0x6c2fdbb1
     
    Karthik Shiraly
    Bartender
    Posts: 1210
    25
    Android Python PHP C++ Java Linux
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I did a bit of investigation into OpenJDK's JVM source code to understand these strange results, and it turns out I was wrong to suggest using jobject as a key.

    Apparently, each JNI thread maintains a an array of 32 slots called "handles". When a native call is being made, the JVM puts any java object argument(s) into a slot,
    and it's the address of the slot that reaches our native method as a jobject. That's the reason your 2 native calls resulted in the same value for the jobject, because
    each call put a different object into the same slot.

    But what exactly is that jobject value?
    It's actually a pointer to an "oop". And an oop is itself another pointer, one to an "oopDesc" struct. Every java object is mirrored in the JVM native code by an oopDesc instance.
    So to get to a java object's native representation via a jobject handle, one has to do


    To summarize, you can't use the jobject itself as a key, because it's just a reused slot. Probably, you can use "long key = *(long*)jobj" because that is the address of the oopDesc that mirrors the java instance
    and should hence be unique. But I'm not sure how GC affects this uniqueness. In any case, it smells like a hack to me. So I think you're better off using hashCode itself, rather than jobject or anything accessible via the jobject . Sorry for the misleading suggestion.

     
    Ron McLeod
    Marshal
    Posts: 4499
    572
    VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks again for your help.

    I'll stick with using hashCode() until I find that it isn't reliable, or I come across a better way. There have been some discussions on SO and other sites regarding multiple instances, but after reading what you have found, it sounds like the suggested solutions may not be not robust. I know that they do not work with the specific case that I have.
     
    Ron McLeod
    Marshal
    Posts: 4499
    572
    VSCode Eclipse IDE TypeScript Redhat MicroProfile Quarkus Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I did find another way using standard JNI API functions which does not depend on hashCode().

    The NewWeakGlobalRef(JNIEnv *env, jobject obj) function creates and returns a reference, which is unique per-instance. Creating the reference does not interfere with later garbage collection, and can be tested to see if the referenced object is slated-for or has already been garbage collected. I am now using these references as the key in to my context array rather than using the hashCode() value to determine if a context for the instance alreay exists, or if a new context needs to be allocated.

    The IsSameObject(JNIEnv *env, jobject ref1, jobject ref2) function can be used with the jobject obj provided with native function calls and the weak reference to test if they are for the same object. I use this to find the existing context for an instance when a native function is called.

    When the reference is no long needed (when close() is called), the DeleteWeakGlobalRef(JNIEnv *env, jweak obj) function deletes the reference. This is safe to call regardless of the state of the instance (garbage collected or not).
     
    Karthik Shiraly
    Bartender
    Posts: 1210
    25
    Android Python PHP C++ Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks for describing the new solution, Ron. Very useful in future.
     
    With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
    reply
      Bookmark Topic Watch Topic
    • New Topic