Skip to content

Conversation

@iklam
Copy link
Member

@iklam iklam commented Sep 9, 2025

The purpose of this PR is to simplify JNI code and also to avoid unnecessary InstanceKlass::cast() calls by adding a new function:

static InstanceKlass* java_lang_Class::as_InstanceKlass(oop java_class);

This PR is intended to be a strict clean-up that preserves existing behaviors.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8367142: Avoid InstanceKlass::cast when converting java mirror to InstanceKlass (Enhancement - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/27158/head:pull/27158
$ git checkout pull/27158

Update a local copy of the PR:
$ git checkout pull/27158
$ git pull https://git.openjdk.org/jdk.git pull/27158/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 27158

View PR using the GUI difftool:
$ git pr show -t 27158

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/27158.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Sep 9, 2025

👋 Welcome back iklam! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Sep 9, 2025

@iklam This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8367142: Avoid InstanceKlass::cast when converting java mirror to InstanceKlass

Reviewed-by: dholmes, coleenp

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 79 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk
Copy link

openjdk bot commented Sep 9, 2025

@iklam The following labels will be automatically applied to this pull request:

  • graal
  • hotspot
  • serviceability

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added graal graal-dev@openjdk.org serviceability serviceability-dev@openjdk.org hotspot hotspot-dev@openjdk.org rfr Pull request is ready for review labels Sep 9, 2025
@mlbridge
Copy link

mlbridge bot commented Sep 9, 2025

Webrevs

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but I think this PR is trying to do too many things at once.

It is pushing JNI resolving inside internal JVM methods, which I think is a bad thing - we resolve JNI references at the boundary to get the oop that the VM wants to work with. Internal APIs should be oblivious to jobject and friends IMO. Also there may be times that the JNI/JVM method needs get the oop itself before extracting the klass.

You are converting klass to instanceKlass where it has to be instanceKlass e.g. with redefinition. This is a good thing, but it is a very distinct thing that deserves its own cleanup (as per previous changes in that area).

You are defining a helper java_lang_Class::as_InstanceKlass to internalize the cast - this is fine but again a simple cleanup that would be better standalone IMO.

It is also not clear that JVM/JNI API's are properly checking that the incoming jobject is in fact a class of the right kind (ie not an array class object).

@iklam
Copy link
Member Author

iklam commented Sep 9, 2025

Sorry but I think this PR is trying to do too many things at once.

It is pushing JNI resolving inside internal JVM methods, which I think is a bad thing - we resolve JNI references at the boundary to get the oop that the VM wants to work with. Internal APIs should be oblivious to jobject and friends IMO. Also there may be times that the JNI/JVM method needs get the oop itself before extracting the klass.

There are 92 header files that have the word jobject in them. There are 71 cpp/hpp files with the word JNIHandles::resolve in them. So I am not sure if we really have that separation anymore.

There are 54 cases where we call as_Klass(JNIHandles::resolve_non_null(x)), so I think it would be nice to have a way to write less code.

You are converting klass to instanceKlass where it has to be instanceKlass e.g. with redefinition. This is a good thing, but it is a very distinct thing that deserves its own cleanup (as per previous changes in that area).

I can move the considering_redefinition changes in a follow-up PR.

You are defining a helper java_lang_Class::as_InstanceKlass to internalize the cast - this is fine but again a simple cleanup that would be better standalone IMO.

It is also not clear that JVM/JNI API's are properly checking that the incoming jobject is in fact a class of the right kind (ie not an array class object).

I am just converting

InstanceKlass* ik = InstanceKlass::cast(as_Klass(mirror));

to

InstanceKlass* ik = as_InstanceKlass(mirror);

The code already assumes that it has an InstanceKlass, and I am not changing that.

@dholmes-ora
Copy link
Member

So I am not sure if we really have that separation anymore.

I think it is more that there are many bits of code that actually form the "boundary" (prims, services, some runtime, jvmci, interpreter-related). But I guess it is hard to argue this makes it markedly worse.

The code already assumes that it has an InstanceKlass, and I am not changing that.

Okay.

@openjdk
Copy link

openjdk bot commented Sep 9, 2025

@iklam this pull request can not be integrated into master due to one or more merge conflicts. To resolve these merge conflicts and update this pull request you can run the following commands in the local repository for your personal fork:

git checkout 8367142-simplify-java-mirror-handling-in-jni-methods
git fetch https://git.openjdk.org/jdk.git master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push

@openjdk openjdk bot added the merge-conflict Pull request has merge conflict with target branch label Sep 9, 2025
@openjdk openjdk bot removed the merge-conflict Pull request has merge conflict with target branch label Sep 9, 2025
@iklam
Copy link
Member Author

iklam commented Sep 9, 2025

So I am not sure if we really have that separation anymore.

I think it is more that there are many bits of code that actually form the "boundary" (prims, services, some runtime, jvmci, interpreter-related). But I guess it is hard to argue this makes it markedly worse.

Arguably the translation of Java mirrors to Klasses is also a boundary (from Java representation to VM representation) :-)

In reality I think because jobjects are easy to use and are just another kind of handle (like Handle and OopHandle), the leakage from JNI code to other parts of VM just happened naturally.

The code already assumes that it has an InstanceKlass, and I am not changing that.

Okay.

BTW I removed the JVMTI changes from this PR.

@dholmes-ora
Copy link
Member

dholmes-ora commented Sep 10, 2025

Arguably the translation of Java mirrors to Klasses is also a boundary (from Java representation to VM representation) :-)

The mirror is an oop, both oop and klass are internal VM representations.

@stefank
Copy link
Member

stefank commented Sep 10, 2025

There are 54 cases where we call as_Klass(JNIHandles::resolve_non_null(x)), so I think it would be nice to have a way to write less code.

I think you should deal with that by adding a helper function inside jni.cpp instead of extending the *Klass functions to take a jobject.

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. A couple of minor nits.

Thanks

Comment on lines 843 to 844
const char * from_name = java_lang_Class::as_Klass(from)->external_name();
const char * to_name = java_lang_Class::as_Klass(result)->external_name();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const char * from_name = java_lang_Class::as_Klass(from)->external_name();
const char * to_name = java_lang_Class::as_Klass(result)->external_name();
const char* from_name = java_lang_Class::as_Klass(from)->external_name();
const char* to_name = java_lang_Class::as_Klass(result)->external_name();

pre-existing nit

Comment on lines 917 to 912
THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null");
THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is primitive");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a behavioural change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted the change to the error message.

I don't know how we will ever get a primitive class in there and who would be reading the error message. I added a comment saying the error message is wrong, so people reading this code will not get confused.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Sep 10, 2025
@coleenp
Copy link
Contributor

coleenp commented Sep 10, 2025

There are 54 cases where we call as_Klass(JNIHandles::resolve_non_null(x)), so I think it would be nice to have a way to write less code.

I think you should deal with that by adding a helper function inside jni.cpp instead of extending the *Klass functions to take a jobject.

I agree with this. I don't think you should move any more jobjects into the runtime code. The jobjects should stop at jni/jvm. They don't everywhere but that's something that over time we should fix. For instance, the ci creates jobjects but it should use OopHandles instead, except per-thread OopStorage isn't implemented yet.

Copy link
Contributor

@coleenp coleenp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jfr also is a boundary between Java -> Native code. That can have jobjects too, but resolve them before calling javaClasses.



Klass* klass() const { return vmClasses::klass_at(klass_id); }
InstanceKlass* klass() const { return vmClasses::klass_at(klass_id); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix the indentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all indentation alignments from this class, as they no longer seem warranted.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Sep 10, 2025
@iklam iklam changed the title 8367142: Simplify java mirror handling in JNI methods 8367142: Avoid InstanceKlass::cast when converting java mirror to InstanceKlass Sep 10, 2025
@iklam
Copy link
Member Author

iklam commented Sep 10, 2025

I didn't realize that my attempt to remove the JNIHandles::resolve() boilerplate can be conversional. I can't put a helper function in jni.cpp because this pattern is used in several files. I've reverted to the old code that makes the explicit calls to JNIHandles::resolve().

I updated the JBS issue text as this PR is now only for reducing the number of InstanceKlass::cast() calls.

Copy link
Member

@dholmes-ora dholmes-ora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced set of changes still looks good.

I was prepared to be accommodating on the broader change, but seems others agreed with my initial position. :)

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Sep 11, 2025
@stefank
Copy link
Member

stefank commented Sep 11, 2025

I didn't realize that my attempt to remove the JNIHandles::resolve() boilerplate can be conversional.

Removing boilerplate wasn't controversial. Spreading the j* types can be seen as controversial give that we have various efforts to push those types out to the boundaries of the JVM. Adding new convenience functions that accept j* goes in the opposite direction.

I can't put a helper function in jni.cpp because this pattern is used in several files.

But almost all are in jni.cpp and jvm.cpp and you can get rid of most of the boilerplate code by adding local helpers there. The handfulish of other places could keep their explicit usage of JNIHandles::resolve* calls.

@iklam
Copy link
Member Author

iklam commented Sep 11, 2025

I didn't realize that my attempt to remove the JNIHandles::resolve() boilerplate can be conversional.

Removing boilerplate wasn't controversial. Spreading the j* types can be seen as controversial give that we have various efforts to push those types out to the boundaries of the JVM. Adding new convenience functions that accept j* goes in the opposite direction.

I can't put a helper function in jni.cpp because this pattern is used in several files.

But almost all are in jni.cpp and jvm.cpp and you can get rid of most of the boilerplate code by adding local helpers there. The handfulish of other places could keep their explicit usage of JNIHandles::resolve* calls.

Maybe in a different PR. I want to keep the current PR simple.

Copy link
Contributor

@coleenp coleenp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. Thanks!

@iklam
Copy link
Member Author

iklam commented Sep 16, 2025

Thanks @dholmes-ora @coleenp for the review
/integrate

@openjdk
Copy link

openjdk bot commented Sep 16, 2025

Going to push as commit 2425584.
Since your change was applied there have been 85 commits pushed to the master branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Sep 16, 2025
@openjdk openjdk bot closed this Sep 16, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Sep 16, 2025
@openjdk
Copy link

openjdk bot commented Sep 16, 2025

@iklam Pushed as commit 2425584.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

graal graal-dev@openjdk.org hotspot hotspot-dev@openjdk.org integrated Pull request has been integrated serviceability serviceability-dev@openjdk.org

Development

Successfully merging this pull request may close these issues.

4 participants