Skip to content

Conversation

@MBaesken
Copy link
Member

@MBaesken MBaesken commented Nov 14, 2025

The dead_strip linker option on macOS removes functions and data that are unreachable by the entry point or exported symbols.
Setting it can reduce the size of some binaries we generate quite a lot, for example (product build, Xcode 15 is used) :
(before -> after setting the option)

1.4M -> 1.1M images/jdk/lib/libfontmanager.dylib
264K -> 248K images/jdk/lib/libjavajpeg.dylib
152K -> 132K images/jdk/lib/libjli.dylib
388K -> 296K images/jdk/lib/liblcms.dylib
164K -> 128K images/jdk/lib/libzip.dylib

and libjvm :

20M -> 18M images/jdk/lib/server/libjvm.dylib
146M -> 137M images/jdk/lib/server/libjvm.dylib.dSYM

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-8371893: [macOS aarch64] use dead_strip linker option to reduce binary size (Bug - P4)

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 28319

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

Using diff file

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

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Nov 14, 2025

👋 Welcome back mbaesken! 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 Nov 14, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk bot changed the title JDK-8371893: [macOS aarch64] use dead_strip linker option to reduce binary size 8371893: [macOS aarch64] use dead_strip linker option to reduce binary size Nov 14, 2025
@openjdk openjdk bot added the build build-dev@openjdk.org label Nov 14, 2025
@openjdk
Copy link

openjdk bot commented Nov 14, 2025

@MBaesken The following label will be automatically applied to this pull request:

  • build

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

@openjdk openjdk bot added the rfr Pull request is ready for review label Nov 14, 2025
@mlbridge
Copy link

mlbridge bot commented Nov 14, 2025

Webrevs

@erikj79
Copy link
Member

erikj79 commented Nov 17, 2025

Is there is a particular reason to not apply this on x64 as well?

@MBaesken
Copy link
Member Author

Is there is a particular reason to not apply this on x64 as well?

On macOS x86_64 I saw in our CI those tests failing when enabling the dead_strip feature
serviceability/sa/ClhsdbCDSCore.java
serviceability/sa/ClhsdbFindPC.jav
serviceability/sa/ClhsdbPstack.java

(and macOS on Intel is not that interesting any more these days)

For macOS aarch64, the tests in our CI were fine but I see some failures in the GHA in the serviceability area.

finding class loader instances ..java.lang.InternalError: Metadata does not appear to be polymorphic
	at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicTypeDataBase.findDynamicTypeForAddress(BasicTypeDataBase.java:223)
	at jdk.hotspot.agent/sun.jvm.hotspot.runtime.VirtualBaseConstructor.instantiateWrapperFor(VirtualBaseConstructor.java:104)
	at jdk.hotspot.agent/sun.jvm.hotspot.oops.Metadata.instantiateWrapperFor(Metadata.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.memory.SystemDictionary.getClassLoaderKlass(SystemDictionary.java:102)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.printClassLoaderStatistics(ClassLoaderStats.java:93)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.run(ClassLoaderStats.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.run(JMap.java:121)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:278)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:241)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:134)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.main(JMap.java:202)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJMAP(SALauncher.java:344)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.main(SALauncher.java:507)

Have to check why I do not see those issues in our test infra.

@erikj79
Copy link
Member

erikj79 commented Nov 18, 2025

Is there is a particular reason to not apply this on x64 as well?

On macOS x86_64 I saw in our CI those tests failing when enabling the dead_strip feature serviceability/sa/ClhsdbCDSCore.java serviceability/sa/ClhsdbFindPC.jav serviceability/sa/ClhsdbPstack.java

That information should at least be stated in the bug for future reference on why this decision was made.

For macOS aarch64, the tests in our CI were fine but I see some failures in the GHA in the serviceability area.

finding class loader instances ..java.lang.InternalError: Metadata does not appear to be polymorphic
	at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicTypeDataBase.findDynamicTypeForAddress(BasicTypeDataBase.java:223)
	at jdk.hotspot.agent/sun.jvm.hotspot.runtime.VirtualBaseConstructor.instantiateWrapperFor(VirtualBaseConstructor.java:104)
	at jdk.hotspot.agent/sun.jvm.hotspot.oops.Metadata.instantiateWrapperFor(Metadata.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.memory.SystemDictionary.getClassLoaderKlass(SystemDictionary.java:102)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.printClassLoaderStatistics(ClassLoaderStats.java:93)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.run(ClassLoaderStats.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.run(JMap.java:121)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:278)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:241)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:134)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.main(JMap.java:202)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJMAP(SALauncher.java:344)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.main(SALauncher.java:507)

Adding serviceability label as this is affecting their tests. I have a vague memory of some seemingly dead code being necessary for certain sa functionality.

/label serviceability

@openjdk openjdk bot added the serviceability serviceability-dev@openjdk.org label Nov 18, 2025
@openjdk
Copy link

openjdk bot commented Nov 18, 2025

@erikj79
The serviceability label was successfully added.

@MBaesken
Copy link
Member Author

I have a vague memory of some seemingly dead code being necessary for certain sa functionality.

Yeah I remember too,
We noticed similar issues when using the lto configure flag on libjvm with gcc (other platform and switch but similar issues).

Is there a way to attribute such 'dead' functions with e.g. an attribute to pragma or something similar to avoid removal of those ?

@MBaesken
Copy link
Member Author

MBaesken commented Nov 18, 2025

There was a bit of discussion on serviceability-dev some years ago about that lto on libjvm issue and the serviceability errors and Magnus commented

'We used to have LTO enabled on the old, closed-source Oracle arm-32 builds. There is still a "link-time-opt" JVM feature present; afaik it still works and adds the -flto flag. The main drawback of this is the extremely long link times of libjvm.so.
I don't think servicability was ever supported for that platform, so I'm not surprised this does not work. '

Probably the dead-strip feature runs into similar issues.

I commented back then
'The serviceability tests like serviceability/sa seems to rely heavily on the "normal" structure of libjvm.so (from what I understand e.g. in LinuxVtblAccess it is attempted to access internal symbols like _ZTV ).'

@theRealAph
Copy link
Contributor

Surely you're going to use features that are provided for debugging. They aren't called from anywhere, but they are used when debugging HotSpot. And you still need them even if you're not using a debug build.

@plummercj
Copy link
Contributor

SA is throwing an exception on the following:

Klass classLoaderKlass = vm.getSystemDictionary().getClassLoaderKlass();

This looks to be the first thing it is doing that involves looking up a hotspot symbol. Possibly the issue is with symbols in general being stripped, or maybe just this one was. The fact that only 3 tests failed makes me think it is some very selective dead stripping of symbols that is the problem. Most SA functionality does not use SystemDictionary().getClassLoaderKlass(), so if just this one symbol was missing, that would explain why for the most part SA is working.

On the assumption that it is just this one symbol, Hotspot declares an array of InstanceKlass* for some critical classes:

InstanceKlass* vmClasses::_klasses[static_cast<int>(vmClassID::LIMIT)]
                                                 =  { nullptr /*, nullptr...*/ };

In SA, the failed SystemDictionary.initialize() part of the stacktrace shows an attempt to access this array for the ClassLoader InstanceKlass*

classLoaderKlassField = type.getAddressField(VM_CLASS_AT("ClassLoader_klass"));

  private static String VM_CLASS_AT(String name) {
    return "_klasses[static_cast<int>(" + VM_CLASS_ID(name) + ")]";
  }

And in vmStructs we have:

static_field(vmClasses, VM_CLASS_AT(ClassLoader_klass), InstanceKlass*) \

I think this vmStructs static field is getting dead stripped.

@plummercj
Copy link
Contributor

Sorry, I think I went somewhat astray in my comments above. I think classLoaderKlassField is being initialized. It's value (an address) is then passed to instantiateWrapperFor():

public static InstanceKlass getClassLoaderKlass() {
return (InstanceKlass)Metadata.instantiateWrapperFor(classLoaderKlassField.getValue());
}

instantiateWrapperFor() is failing. If you look at the stack trace (and the accompanying code), it appears that it has failed to lookup the base type, which is Metadata. There probably are no instances of Metadata allocated, just instances of subclasses, so possibly the Metadata vtbl has been dead stripped, and SA needs it. However, I would think this would result in mass failures since we call Metadata.instantiateWrapperFor() all over the place.

If you want to attempt a fix, have hotspot allocate a Metadata instance somewhere and keep a reference to it. That should prevent the Metadata vtbl from being dead stripped.

@mrserb
Copy link
Member

mrserb commented Nov 18, 2025

As usual, maybe it is possible to split this patch between hotspot and the libraries?

@plummercj
Copy link
Contributor

I reproduced the SA issues locally. Not surprisingly almost every SA test failed (I'm not sure why only 3 were listed above). They seemed to all fail trying to get a vtable for a hotspot type. The tests that didn't failed were doing mundane things like testing attaching, hotspot flags, and clhsdb history, but not doing anything with hotspot objects, or at least not with hotspot metadata.

I was able to confirm the problem is with the Metadata vtable missing. I fixed it by allocating a Metadata instance and assigning it to a global. However, in order to get this to compile I had make Metadata no longer be abstract. All tests passed after I did this. I'm not sure how viable a solution this is. The hotspot team will probably object to the Metadata changes. Maybe there is some other way to cause a reference to the Metadata vtable so it is not dead stripped.

@MBaesken
Copy link
Member Author

Maybe there is some other way to cause a reference to the Metadata vtable so it is not dead stripped.

Would be good to have a fix people agree on, because we already saw the issue with lto enabled for Hotspot (see my comment above). So it is not only an issue related to the dead_strip flag.

@MBaesken
Copy link
Member Author

As usual, maybe it is possible to split this patch between hotspot and the libraries?

For sure we can do this.
But as listed above, we only see for a few jdk native libs a good size benefit when using this additional flag.

@MBaesken
Copy link
Member Author

Is there is a particular reason to not apply this on x64 as well?

On macOS x86_64 I saw in our CI those tests failing when enabling the dead_strip feature serviceability/sa/ClhsdbCDSCore.java serviceability/sa/ClhsdbFindPC.jav serviceability/sa/ClhsdbPstack.java

(and macOS on Intel is not that interesting any more these days)

For macOS aarch64, the tests in our CI were fine but I see some failures in the GHA in the serviceability area.

finding class loader instances ..java.lang.InternalError: Metadata does not appear to be polymorphic
	at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicTypeDataBase.findDynamicTypeForAddress(BasicTypeDataBase.java:223)
	at jdk.hotspot.agent/sun.jvm.hotspot.runtime.VirtualBaseConstructor.instantiateWrapperFor(VirtualBaseConstructor.java:104)
	at jdk.hotspot.agent/sun.jvm.hotspot.oops.Metadata.instantiateWrapperFor(Metadata.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.memory.SystemDictionary.getClassLoaderKlass(SystemDictionary.java:102)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.printClassLoaderStatistics(ClassLoaderStats.java:93)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.ClassLoaderStats.run(ClassLoaderStats.java:78)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.run(JMap.java:121)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:278)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:241)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:134)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JMap.main(JMap.java:202)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJMAP(SALauncher.java:344)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.main(SALauncher.java:507)

Have to check why I do not see those issues in our test infra.

Okay I checked why this is the case.
In our internal test setup we see some serviceability/sa - tests skipped because of 'SA Attach not expected to work. Insufficient privileges' . This can happen in some test setups especially on macOS but is not very helpful to test the influence of the dead_strip flag on the sa tests .

@theRealAph
Copy link
Contributor

I remember s390 used to strip unneeded stuff, and it was very hard to debug anything. Please think about whether you need to do this.

@MBaesken
Copy link
Member Author

I remember s390 used to strip unneeded stuff, and it was very hard to debug anything.

What exactly was hard to debug?
Did you use (fast)debug or product binaries ?
I do not remember issues like this on s390 but I can ask my (ex)colleagues who dealt more with s390 .

@MBaesken
Copy link
Member Author

MBaesken commented Nov 19, 2025

Btw on Linux s390x it is the linktime-gc , but from what I see this can be disabled by configure if you don't like it

UTIL_ARG_ENABLE(NAME: linktime-gc, DEFAULT: $LINKTIME_GC_DEFAULT,

if test "x$ENABLE_LINKTIME_GC" = xtrue; then

It is just enabled by default on Linux s390x because there we have no serviceability agent support.

@MBaesken
Copy link
Member Author

MBaesken commented Nov 19, 2025

I reproduced the SA issues locally. Not surprisingly almost every SA test failed (I'm not sure why only 3 were listed above). They seemed to all fail trying to get a vtable for a hotspot type. The tests that didn't failed were doing mundane things like testing attaching, hotspot flags, and clhsdb history, but not doing anything with hotspot objects, or at least not with hotspot metadata.

I was able to confirm the problem is with the Metadata vtable missing. I fixed it by allocating a Metadata instance and assigning it to a global. However, in order to get this to compile I had make Metadata no longer be abstract. All tests passed after I did this. I'm not sure how viable a solution this is. The hotspot team will probably object to the Metadata changes. Maybe there is some other way to cause a reference to the Metadata vtable so it is not dead stripped.

There seem to be attributes for this kind of use case in clang, e.g. retain
https://reviews.llvm.org/D97447
https://clang.llvm.org/docs/AttributeReference.html#retain
Should we add this to 'class Metadata' ?
If we can do it with attributes or something similar (and without changing lots of code locations) it sounds better to me than adjusting the coding.
'Pseudo dead' stuff that is used from external tools (so it is not really 'dead' in practise) should be somehow marked to assist the tools.
There is also an attribute 'used' that looks similar.

@theRealAph
Copy link
Contributor

On 19/11/2025 13:22, Matthias Bäsken wrote:

MBaesken left a comment (#28319)

I remember s390 used to strip unneeded stuff, and it was very hard to debug anything.

What exactly was hard to debug?
Did you use (fast)debug or product binaries ?
I do not remember issues like this on s390 but I can ask my (ex)colleagues who dealt more with s390 .

It was hard to debug hotspot. Helpers such as pp(), back_trace(), and pfl() were missing.

I think it was a release build, but of course you sometimes have to debug release builds.

@theRealAph
Copy link
Contributor

Btw on Linux s390x it is the linktime-gc , but from what I see this can be disabled by configure if you don't like it

Yes, it can. Thankfully I don't usually need to debug hotspot in production s390 systems. I do, however, need to debug AArch64 on MacOS. This is a Very Bad Idea, and should not be dome.

@MBaesken
Copy link
Member Author

Seems like we should fix the elimination of those debug helpers ; but those are only in the jvm lib. The situation in other libs is different.

@plummercj
Copy link
Contributor

There seem to be attributes for this kind of use case in clang, e.g. retain
https://reviews.llvm.org/D97447
https://clang.llvm.org/docs/AttributeReference.html#retain
Should we add this to 'class Metadata' ?
If we can do it with attributes or something similar (and without changing lots of code locations) it sounds better to me than adjusting the coding.
'Pseudo dead' stuff that is used from external tools (so it is not really 'dead' in practise) should be somehow marked to assist the tools.
There is also an attribute 'used' that looks similar.

I tried the following but it didn't work. Perhaps I don't have the syntax right:

--- a/src/hotspot/share/oops/metadata.hpp
+++ b/src/hotspot/share/oops/metadata.hpp
@@ -30,6 +30,7 @@
 #include "utilities/ostream.hpp"
 
 // This is the base class for an internal Class related metadata
+#pragma retain Metadata
 class Metadata : public MetaspaceObj {
   // Debugging hook to check that the metadata has not been deleted.
   NOT_PRODUCT(int _valid;)

Note retain and used are listed under function attributes. They probably don't apply to data.

@plummercj
Copy link
Contributor

The following is how you declare a reference to the vtable:

extern "C" void* _ZTV8Metadata[];

If you then reference _ZTV8Metadata from somewhere in a way that does not get dead stripped, that seems to fix the problem. The latter part is definitely very hacky. It would be nice to get a pragma working to keep the symbol from being deadstripped.

@plummercj
Copy link
Contributor

This seems to work:

extern void* _ZTV8Metadata[];

__attribute__((used))
 void* foo() {
  return _ZTV8Metadata[0];
}

You can put this anywhere since it does not reference any hotspot types. It just needs to be linked in with libjvm. Note I tried putting the "used" attribute on _ZTV8Metadata and getting rid of the foo() part, but I got the following warning and the vtable was dead stripped:

warning: 'used' attribute ignored on a non-definition declaration [-Wignored-attributes]

@theRealAph
Copy link
Contributor

Seems like we should fix the elimination of those debug helpers ; but those are only in the jvm lib. The situation in other libs is different.

Maybe, but it's not just the specific debug helpers. There are many functions in hotspot, and the maintenance programmer (in the case of Mac/AArch64, that's often me) will frequently type stuff like print a->size().

Linker options that strip "unused" code in binaries have been available for decades, and we haven't much used such options because they're a pain. Debugging on MacOS/AArch64 is a pain as it is, without making it worse.

@theRealAph
Copy link
Contributor

On further consideration, I remembered that many of the helpers are debug-only, so that shouldn't be a problem. Maybe if we only do this on non-PRODUCT builds it's be OK, but it would require very careful thought and analysis.

@MBaesken
Copy link
Member Author

On further consideration, I remembered that many of the helpers are debug-only, so that shouldn't be a problem

Thanks for looking into it. I can disable the dead_strip switch for the debug-builds, so it would not eliminate these helpers.

@MBaesken
Copy link
Member Author

This seems to work:

extern void* _ZTV8Metadata[];

__attribute__((used))
 void* foo() {
  return _ZTV8Metadata[0];
}

You can put this anywhere since it does not reference any hotspot types. It just needs to be linked in with libjvm. Note I tried putting the "used" attribute on _ZTV8Metadata and getting rid of the foo() part, but I got the following warning and the vtable was dead stripped:

warning: 'used' attribute ignored on a non-definition declaration [-Wignored-attributes]

Thanks for the testing and providing the workaround. Too bad that the 'retain' and 'used' are only function attributes, this makes things a bit harder.

@openjdk openjdk bot added the hotspot hotspot-dev@openjdk.org label Nov 20, 2025
@openjdk
Copy link

openjdk bot commented Nov 20, 2025

@MBaesken hotspot has been added to this pull request based on files touched in new commit(s).

@theRealAph
Copy link
Contributor

On further consideration, I remembered that many of the helpers are debug-only, so that shouldn't be a problem

Thanks for looking into it. I can disable the dead_strip switch for the debug-builds, so it would not eliminate these helpers.

Right, but we'll need to make sure we don't remove the non-debug ones. We don't want to exclude stuff in src/hotspot/share/utilities/debug.cpp or explicit helpers in the back ends.

@MBaesken
Copy link
Member Author

Interestingly, we seem to rely already on the 'used' attribute in the OpenJDK codebase at some places, see
https://github.com/search?q=repo%3Aopenjdk%2Fjdk%20ATTRIBUTE_USED&type=code

@MBaesken
Copy link
Member Author

For the Apple linker there seem to be also a way to mark some sections with S_ATTR_NO_DEAD_STRIP to avoid the dead strip operation
https://maskray.me/blog/2021-02-28-linker-garbage-collection
'Sections with the S_ATTR_NO_DEAD_STRIP flag'
But I am not sure how to do this in the HS code directly.

@plummercj
Copy link
Contributor

On further consideration, I remembered that many of the helpers are debug-only, so that shouldn't be a problem

Thanks for looking into it. I can disable the dead_strip switch for the debug-builds, so it would not eliminate these helpers.

Right, but we'll need to make sure we don't remove the non-debug ones. We don't want to exclude stuff in src/hotspot/share/utilities/debug.cpp or explicit helpers in the back ends.

Most of the debug.cpp helpers are in product builds now. I think only pns() and pns2() are left out of product builds.

@MBaesken
Copy link
Member Author

MBaesken commented Nov 21, 2025

As usual, maybe it is possible to split this patch between hotspot and the libraries?

Maybe we should for now limit the dead_strip to the JDK native libs ?

This would avoid the trouble with the HS debug helpers coding and the serviceability agent (but in the long run we have this trouble also with linktime-gc and probably LTO which can be enabled by OpenJDK configure, not only with this macOS-linker related flag this PR is about ).

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

Labels

build build-dev@openjdk.org hotspot hotspot-dev@openjdk.org rfr Pull request is ready for review serviceability serviceability-dev@openjdk.org

Development

Successfully merging this pull request may close these issues.

5 participants