Skip to content

Conversation

@coleenp
Copy link
Contributor

@coleenp coleenp commented May 9, 2024

This change stores InstanceKlass for interface and abstract classes in the non-class metaspace, since class metaspace will have limits on number of classes that can be represented when Lilliput changes go in. Classes that have no instances created for them don't require compressed class pointers. The generated LambdaForm classes are also AllStatic, and changing them to abstract moves them to non-class metaspace too. It's not technically great to make them abstract and not final but you can't have both. Java classfile access flags have no way of specifying something like AllStatic.

Tested with tier1-8.


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-8338526: Don't store abstract and interface Klasses in class metaspace (Enhancement - P4)

Reviewers

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 19157

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

Using diff file

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

Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented May 9, 2024

👋 Welcome back coleenp! 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 May 9, 2024

@coleenp 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:

8338526: Don't store abstract and interface Klasses in class metaspace

Reviewed-by: stuefe, iklam

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 155 new commits pushed to the master branch:

  • 0d8e52b: 8339800: Prefer invokeBasic in BootstrapMethodInvokers
  • 64de781: 8339587: runtime/reflect/ReflectOutOfMemoryError.java fails with "bootstrap method initialization exception"
  • 125f743: 8305489: runtime/ErrorHandling/TestDwarf.java fails in some Linux configurations after JDK-8303805
  • 7e2bcf6: 8338890: Add monitoring/management interface for the virtual thread scheduler
  • 5e822c2: 8334870: javac does not accept classfiles with certain permitted RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes
  • 56387a0: 8329754: The ThreadSafe attribute is ignored for SecureRandom algorithm aliases
  • 559fc71: 8339366: [jittester] Make it possible to generate tests without execution
  • 6b5958d: 8339696: Clarify modeling scope of javax.lang.model.element
  • 77468c2: 8339575: DumpingWithJavaAgent.java failed with missing expected output
  • 86a2f9c: 8339644: Improve parsing of Day/Month in tzdata rules
  • ... and 145 more: https://git.openjdk.org/jdk/compare/daf26178be07bfe4a46592bcde092ce297a092bb...master

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 May 9, 2024

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

  • core-libs
  • hotspot

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 hotspot hotspot-dev@openjdk.org core-libs core-libs-dev@openjdk.org labels May 9, 2024
@openjdk
Copy link

openjdk bot commented Jun 24, 2024

@coleenp 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 anon
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 Jun 24, 2024
@openjdk openjdk bot removed the merge-conflict Pull request has merge conflict with target branch label Aug 16, 2024
@bridgekeeper
Copy link

bridgekeeper bot commented Aug 19, 2024

@coleenp This pull request has been inactive for more than 8 weeks and will be automatically closed if another 8 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration!

@coleenp coleenp changed the title Store interface and abstract classes in non-class metaspace. Also, make LambdaForm generated classes abstract. 8338526: Don't store abstract and interface Klasses in class metaspace Aug 21, 2024
@coleenp coleenp marked this pull request as ready for review August 21, 2024 16:34
@openjdk openjdk bot added the rfr Pull request is ready for review label Aug 21, 2024
@mlbridge
Copy link

mlbridge bot commented Aug 21, 2024

Webrevs

Copy link
Member

@liach liach left a comment

Choose a reason for hiding this comment

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

java.lang.invoke changes look good.

@rkennke
Copy link
Contributor

rkennke commented Aug 22, 2024

If I understand correctly, we already have limits on how many classes we can represent as compressed class-pointers. While this is nice for Lilliput, this is equally useful for non-Lilliput CCP, because addressable class-space doesn't get polluted by classes that never need to be encoded as CCP, and thus effectively increases the number of classes that we can address without resorting to -UseCompressedClassPointers.

@tstuefe
Copy link
Member

tstuefe commented Aug 22, 2024

I am surprised that this patch is so small. I would have assumed a lot of code exists that unconditionally assumes we always can encode decode Klass*<->narrowKlass.

I looked through the typical cases (eg Klass validation) and all of them seem to be okay. I will keep looking.

@coleenp
Copy link
Contributor Author

coleenp commented Aug 22, 2024

Thanks for looking at this @tstuefe. I was pleased that the change was small but once we identify the classes as AbstractClass instead of Class in metaspace, it just falls out. I thought CDS would have more changes, but CDS is all in one space and doesn't differentiate. It's good that the only time we compress and uncompress klass pointers is when we get them out of an object and this should keep it that way.

Copy link
Member

@tstuefe tstuefe left a comment

Choose a reason for hiding this comment

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

Hi Coleen,

IIUC, the new "is_in_klass_space" function that now is present in all Metadata children only exists because of the template function in MetadataFactory, right? Just for the purpose of deallocation?

If so, see this proposed addition to your patch: https://gist.github.com/tstuefe/5111c735b12f6d9c3c1d32699d0820f6

This would make Metaspace::deallocate smarter - Metaspace knows whether a given pointer is in class space or not, it can do automatically the right thing. There should be no need to tell it how to deallocate that storage. (If you are worried, in debug builds there are also sanity checks).

If you do this, I think you could remove all variants of "is_in_klass_space" apart from the one in Klass.

@coleenp
Copy link
Contributor Author

coleenp commented Aug 23, 2024

Yes, is_in_klass_space was just to direct where to deallocate the metaspace pointer. In your patch isn't the contains metaspace call still very slow? Or I suppose for class space, it's not because it's a fixed space. But it's not an inlined call at all because I had to search in cpp files for the range check.

  • const bool is_class = Metaspace::contains_in_class_space(ptr);

I sort of think it might be better for the outside runtime code to control this and the metaspace call assert if its wrong.

Copy link
Contributor Author

@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.

Thanks for reviewing this and your comments Thomas.

@tstuefe
Copy link
Member

tstuefe commented Aug 24, 2024

Yes, is_in_klass_space was just to direct where to deallocate the metaspace pointer. In your patch isn't the contains metaspace call still very slow? Or I suppose for class space, it's not because it's a fixed space. But it's not an inlined call at all because I had to search in cpp files for the range check.

  • const bool is_class = Metaspace::contains_in_class_space(ptr);

I sort of think it might be better for the outside runtime code to control this and the metaspace call assert if its wrong.

No, I think my way is better and it will be needed anyway for TinyCP/Lilliput. We only need to do two address comparisons, that should be simple and fast.

I opened a PR to separate the change, and in that PR I also inline the check.

#20701

I don't think the costs for two address comparisons matter, not with the comparatively few deallocations that happen (few hundreds or few thousand). If deallocate is hot, we are using metaspace wrong.

@tstuefe
Copy link
Member

tstuefe commented Aug 24, 2024

I renamed this is_in_class_space() with the lower case 'c'. It's still directing metaspace or indicating where the object was allocated. Your name is a little better but I think not enough until we want to expand the things we want allocated in the class space. As we talked about, with Tiny Class Pointers, class space will have different things in it (not that these new things need a compressed pointers). But I think we're better off having less things in the space where their pointers can be compressed since this space is constrained.

How about "needs_class_space" then?

@coleenp
Copy link
Contributor Author

coleenp commented Aug 26, 2024

I don't think the costs for two address comparisons matter, not with the comparatively few deallocations that happen (few hundreds or few thousand). If deallocate is hot, we are using metaspace wrong.

MethodData does a lot of deallocations from metaspace because it's allocated racily. It might be using Metaspace wrong.

@tstuefe
Copy link
Member

tstuefe commented Aug 27, 2024

I don't think the costs for two address comparisons matter, not with the comparatively few deallocations that happen (few hundreds or few thousand). If deallocate is hot, we are using metaspace wrong.

MethodData does a lot of deallocations from metaspace because it's allocated racily. It might be using Metaspace wrong.

I think that should be okay. This should still be an exception. I have never seen that many deallocations happening in customer cases.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Aug 29, 2024
@coleenp
Copy link
Contributor Author

coleenp commented Sep 3, 2024

@tstuefe Do you have more comments on this PR?

@tstuefe
Copy link
Member

tstuefe commented Sep 3, 2024

@tstuefe Do you have more comments on this PR?

Sorry, I was swamped the past days. I'll take a look tomorrow.

Copy link
Member

@tstuefe tstuefe left a comment

Choose a reason for hiding this comment

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

I still worry about unforeseen implications of not every Klass having an nKlass. It will have some implications on some of my Lilliput work. I wish we could have done this earlier.

But in any case, this seems to be a reasonable way forward. One remark inline, otherwise this looks mostly good to me.

static bool is_compressed_klass_ptr(const Klass* k) {
return using_class_space() && (is_in_class_space(k) || is_in_shared_metaspace(k));
}

Copy link
Member

Choose a reason for hiding this comment

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

I propose to drop this, and instead add a utility function to CompressedKlassPointers like this:

// Returns true if p falls into the narrow Klass encoding range
inline bool CompressedKlassPointers::is_in_encoding_range(const void* p) {
  return _base != nullptr && p >= _base && p < (_base + _range);
}

(Probably the _base != nullptr could even be left out, since _range==0 and _base==nullptr for -UseCompressedClassPointers)

And then use that function in jfrTraceIdKlass.cpp. That file needs to use CompressedKlassPointers anyway because it needs to encode the Klass*.

This avoids having to rely on the exact composition of the memory regions inside the encoding range. What counts is whether the Klass pointer points into the narrow Klass encoding range.

Essentially, with CDS, memory looks like this:

encoding                                                               encoding              
base                                                                   end
|-----------------------------------------------------------------------|
|----CDS---| |--------------------class space---------------------------|

Copy link
Contributor Author

@coleenp coleenp Sep 6, 2024

Choose a reason for hiding this comment

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

oh yes that's much better and more precise. _base != nullptr isn't needed because p == nullptr will still return false for p < _base + range (both zero).
Thanks for the ascii art!

Copy link
Member

@tstuefe tstuefe 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 good to me.

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

coleenp commented Sep 10, 2024

Thanks for reviewing Ioi and Thomas, and thank you Thomas for the suggested changes.
/integrate

@openjdk
Copy link

openjdk bot commented Sep 10, 2024

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

  • 0d8e52b: 8339800: Prefer invokeBasic in BootstrapMethodInvokers
  • 64de781: 8339587: runtime/reflect/ReflectOutOfMemoryError.java fails with "bootstrap method initialization exception"
  • 125f743: 8305489: runtime/ErrorHandling/TestDwarf.java fails in some Linux configurations after JDK-8303805
  • 7e2bcf6: 8338890: Add monitoring/management interface for the virtual thread scheduler
  • 5e822c2: 8334870: javac does not accept classfiles with certain permitted RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes
  • 56387a0: 8329754: The ThreadSafe attribute is ignored for SecureRandom algorithm aliases
  • 559fc71: 8339366: [jittester] Make it possible to generate tests without execution
  • 6b5958d: 8339696: Clarify modeling scope of javax.lang.model.element
  • 77468c2: 8339575: DumpingWithJavaAgent.java failed with missing expected output
  • 86a2f9c: 8339644: Improve parsing of Day/Month in tzdata rules
  • ... and 145 more: https://git.openjdk.org/jdk/compare/daf26178be07bfe4a46592bcde092ce297a092bb...master

Your commit was automatically rebased without conflicts.

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

openjdk bot commented Sep 10, 2024

@coleenp Pushed as commit ad10493.

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

@theRealAph
Copy link
Contributor

theRealAph commented Oct 8, 2024 via email

@theRealAph
Copy link
Contributor

theRealAph commented Oct 9, 2024 via email

@merykitty
Copy link
Member

merykitty commented Oct 9, 2024

@theRealAph I do not know your idea regarding this matter but does compressing interface pointers independently from concrete class pointers help?

@rose00
Copy link
Contributor

rose00 commented Oct 9, 2024

If the interfaces had a compact numbering, and there were a side table mapping the compact numbers to interface Klass* pointers, then I think Andrew's code would still work (with natural adjustments).

But managing such a side table is at least as complicated as just doing the pointer compression stuff we do. (Hence my comment that we haven't had to invent side tables yet.)

Because of CDS I don't think we can treat concretes and abstracts (or even just classes and interfaces) as disjoint metadata types with separate independent compression tactics or representations. I think we need a subtype/supertype relation between the "narrowest" and merely "narrower" klass IDs.

@rkennke
Copy link
Contributor

rkennke commented Oct 9, 2024

AFAIK, @tstuefe (currently on vacation) has a working prototype of a Klass-lookup-table with good performance and reasonable ‘management cost’. This would make make many things much simpler and also help solve this problem because it makes irrelevant where a Klass lives.

Roman

@theRealAph
Copy link
Contributor

theRealAph commented Oct 9, 2024 via email

@theRealAph
Copy link
Contributor

@theRealAph I do not know your idea regarding this matter but does compressing interface pointers independently from concrete class pointers help?

Of course that is possible, but it add complexity; and my goal is reducing complexity.

@theRealAph
Copy link
Contributor

If the interfaces had a compact numbering, and there were a side table mapping the compact numbers to interface Klass* pointers, then I think Andrew's code would still work (with natural adjustments).

Probably, yes. I can pack klass ID+ [iv]table offset into a word to identify a method, or use a compressed metadata pointer. But I have a data dependency on a table load to get from a compressed Klass ID of some sort to a Klass*, that's a 3-to-5-cycle stall.

But managing such a side table is at least as complicated as just doing the pointer compression stuff we do. (Hence my comment that we haven't had to invent side tables yet.)

Because of CDS I don't think we can treat concretes and abstracts (or even just classes and interfaces) as disjoint metadata types with separate independent compression tactics or representations. I think we need a subtype/supertype relation between the "narrowest" and merely "narrower" klass IDs.

I think that's right.

Notwithstanding any side tables etc., it would be nice to make sure that all metadata is reachable with a 4Gbyte offset from some base. It's not so hard to make sure that concrete and abstract Klass* (and all Method*s) can be in that range, simply(?) by allocating everything contiguously.

@rose00
Copy link
Contributor

rose00 commented Oct 9, 2024

simply(?) by allocating everything contiguously

Suddenly I seem to hear Boromir and Yoda, in unison, saying, "One does not simply."

@theRealAph
Copy link
Contributor

AFAIK, @tstuefe (currently on vacation) has a working prototype of a Klass-lookup-table with good performance and reasonable ‘management cost’. This would make make many things much simpler and also help solve this problem because it makes irrelevant where a Klass lives.

Yeah, I get that, and I'm sure @tstuefe has done a great job. But it goes to the fundamental theorem of software engineering (FTSE), Wheeler's statement that "We can solve any problem by introducing an extra level of indirection," often followed by "…except for the problem of too many levels of indirection."

@rose00
Copy link
Contributor

rose00 commented Oct 9, 2024

Sometimes, if you are very clever and determined, you can do the very non-simple thing of putting some items at the least level of indirection, and other items at further levels of indirection. You'd try to put stuff you need frequently closer to the root address.

Currently everything is equally close to the root address, except that IIRC we try to put a few really performance sensitive things on the earliest cache lines.

Splitting one Klass structure into a near and a far part is doable in principle, but the complexity adds up quickly, and there is also a problem deciding what to put in the near part.

Specifically, you probably want some v-table entries, but not all v-table entries, in the near part. (Why not all? Because near space is relatively scarce. The near part might even be fixed in size, depending on design.)

The bottom line for me: I like flatter data and shorter indirection chains. The main exception is that I ALSO like to separate hot from cold data, when the opportunity arises, and cold data is often at the end of an unused indirection.

(I think there is relatively little cold data in metaspace, and it is probably already packed inside a pointer to an Array.)

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

Labels

core-libs core-libs-dev@openjdk.org hotspot hotspot-dev@openjdk.org integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

10 participants