https://bz.apache.org/bugzilla/show_bug.cgi?id=70305
Bug ID: 70305
Summary: FFM OpenSSLEngine creates an upcall stub per engine,
causing per-connection hidden-class generation and GC
degradation
Product: Tomcat 10
Version: 10.1.61
Hardware: All
OS: All
Status: NEW
Severity: normal
Priority: P2
Component: Connectors
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ------
Created attachment 40243
--> https://bz.apache.org/bugzilla/attachment.cgi?id=40243&action=edit
Test class to reproduce
The OpenSSLEngine constructor allocates a new upcall stub for the info callback
on every engine, i.e. on every TLS connection:
engineArena = Arena.ofAuto();
...
SSL_set_info_callback(ssl, SSL_set_info_callback$cb.allocate(new
InfoCallback(), engineArena));
Each stub wraps its own bound MethodHandle. The JDK's upcall path invokes that
handle through an exact invoker, so after a handle has been called
MethodHandle.CUSTOMIZE_THRESHOLD (127) times, the JVM compiles a LambdaForm
customized for that specific handle instance, which defines a new hidden class.
OpenSSL calls the info callback many times per connection (every handshake
state
change and alert), so long-lived or busy connections cross the threshold, and
each one leaves behind its own hidden class. Those classes can only be
reclaimed
by class unloading after the engine's auto arena is collected.
Under sustained traffic this produces continuous class churn.
Standalone reproducer:
Attached is a program that mirrors what OpenSSLEngine does: one upcall stub per
"engine", each called N times from native code. Passing "shared" uses one stub
for all engines instead.
javac UpcallInvoke.java
java --enable-native-access=ALL-UNNAMED UpcallInvoke per 200
java --enable-native-access=ALL-UNNAMED UpcallInvoke shared 200
Results on JDK 25:
stub per engine, 2000 engines x 10 callbacks: classes loaded=85
stub per engine, 2000 engines x 100 callbacks: classes loaded=85
stub per engine, 2000 engines x 200 callbacks: classes loaded=4086
shared stub, 2000 engines x 200 callbacks: classes loaded=12
Possible fix:
InfoCallback holds no per-engine state: it looks up the EngineState by the SSL*
address in the static `states` map. A single stub can therefore serve every
engine. For example, allocate it once in a static field (Arena.global()), or
once
per OpenSSLContext in contextArena alongside the existing context-level
callbacks, and pass it to SSL_set_info_callback in the constructor.
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]