Jinwoo Hwang created GEODE-10522:
------------------------------------
Summary: Eliminate VMStats50 Reflection to Remove --add-opens JVM
Flag Requirement
Key: GEODE-10522
URL: https://issues.apache.org/jira/browse/GEODE-10522
Project: Geode
Issue Type: Improvement
Reporter: Jinwoo Hwang
Assignee: Jinwoo Hwang
Fix For: 2.1.0
h2. Problem Description
h3. Current State
The {{AddressableMemoryManager}} class in {{geode-core}} currently uses
reflection to access private internal APIs of {{{}java.nio.DirectByteBuffer{}}}:
# {{DirectByteBuffer.address()}} method - to retrieve the native memory
address from a DirectByteBuffer
# {{DirectByteBuffer(long, int)}} constructor - to create a DirectByteBuffer
wrapping a native memory address
This reflection-based approach requires calling {{setAccessible(true)}} on both
the method and constructor, which violates Java's module encapsulation rules
introduced in Java 9 (JEP 260, 261).
h3. Required JVM Flag
To allow this reflection at runtime, Apache Geode currently requires the
following JVM flag:
{code:java}
--add-opens=java.base/java.nio=ALL-UNNAMED
{code}
This flag is hardcoded in {{MemberJvmOptions.JAVA_11_OPTIONS}} as
{{JAVA_NIO_OPEN}} and automatically added to all Geode member JVMs running on
Java 11 and later.
h3. Code Evidence
Current implementation in {{{}AddressableMemoryManager.java{}}}:
{code:java}
// Reflection-based access requiring --add-opens
private static volatile Method dbbAddressMethod = null;
private static volatile Constructor dbbCtor = null;
public static long getDirectByteBufferAddress(ByteBuffer bb) {
Method m = dbbAddressMethod;
if (m == null) {
m = dbbClass.getDeclaredMethod("address");
m.setAccessible(true); // REQUIRES --add-opens
dbbAddressMethod = m;
}
return (Long) m.invoke(bb);
}
static ByteBuffer createDirectByteBuffer(long address, int size) {
Constructor ctor = dbbCtor;
if (ctor == null) {
ctor = dbbClass.getDeclaredConstructor(long.class, int.class);
ctor.setAccessible(true); // REQUIRES --add-opens
dbbCtor = ctor;
}
return (ByteBuffer) ctor.newInstance(address, size);
}
{code}
h2. Business Impact
h3. Security and Compliance Issues
h4. Java Platform Security Model Violations
The use of {{--add-opens}} flags represents a security concern and technical
debt:
* {*}Strong Encapsulation Bypass{*}: The flag explicitly breaks Java's module
system encapsulation, which was designed to protect internal implementation
details
* {*}Security Manager Conflicts{*}: In environments with security managers or
restricted permissions, these flags may be blocked or flagged as security
violations
* {*}Audit Trail Issues{*}: Security audits and compliance scans flag
{{--add-opens}} usage as potential vulnerabilities
* {*}Attack Surface{*}: Opening internal packages to ALL-UNNAMED increases the
attack surface by allowing any code to access these internals
h4. Deployment and Operations Impact
The required JVM flag creates operational friction:
* {*}Container Security Policies{*}: Many container platforms (Kubernetes,
Docker Enterprise) have security policies that restrict or audit JVM flags
* {*}Cloud Platform Restrictions{*}: Some cloud platforms (AWS Lambda, Azure
Functions, GCP Cloud Run) restrict or monitor JVM flags
* {*}Enterprise Security Policies{*}: Enterprise security teams often require
justification and approval for {{--add-opens}} flags
* {*}Deployment Complexity{*}: Administrators must remember to include the
flag in all deployment configurations
h4. Forward Compatibility Risk
Java's module system evolution poses risks:
* {*}JEP 403 (Java 17+){*}: Strongly encapsulates JDK internals by default,
with warnings issued for illegal reflective access
* {*}Future Java Versions{*}: The Java platform is moving toward stricter
enforcement of module boundaries
* {*}Deprecation Path{*}: {{--add-opens}} may become deprecated or restricted
in future Java releases
* {*}Migration Cost{*}: The longer this technical debt remains, the more
costly it becomes to remediate
h3. Technical Impact
h4. Off-Heap Memory Management
{{AddressableMemoryManager}} is a critical component in Geode's off-heap memory
management:
* {*}Purpose{*}: Manages direct ByteBuffer access to off-heap memory regions
* {*}Performance Critical{*}: Used in hot paths for off-heap data access
* {*}Memory Safety{*}: Provides abstraction layer over native memory addresses
* {*}Zero-Copy Operations{*}: Enables wrapping native memory addresses as
ByteBuffers without copying data
Current usage patterns:
{code:java}
// Get address from a DirectByteBuffer
long address = AddressableMemoryManager.getDirectByteBufferAddress(buffer);
// Create a ByteBuffer wrapping a native memory address
ByteBuffer wrapper = AddressableMemoryManager.createDirectByteBuffer(address,
size);
{code}
h4. Dependency Chain
The {{java.nio}} module opening affects multiple components:
* {{{}geode-core{}}}: Direct usage in {{AddressableMemoryManager}}
* {{{}geode-gfsh{}}}: Automatic flag injection via {{MemberJvmOptions}}
* All Geode members: Every server, locator, and client requires the flag
* Test infrastructure: All integration and unit tests must include the flag
h2. Benefits of Remediation
h3. Security and Compliance
* {*}Eliminates Security Audit Findings{*}: Removes {{--add-opens}} flag from
security scan results
* {*}Improves Security Posture{*}: Operates within Java's intended security
model
* {*}Simplifies Compliance{*}: Reduces justification burden for security teams
* {*}Future-Proof Security{*}: Aligns with Java platform security evolution
h3. Operational Excellence
* {*}Simplified Deployment{*}: One less JVM flag to configure and document
* {*}Cloud-Native Friendly{*}: Compatible with restrictive cloud platform
policies
* {*}Container Security{*}: Passes stricter container security policies
without exceptions
* {*}Reduced Configuration Complexity{*}: Fewer deployment-specific
configurations to maintain
h3. Java Platform Compatibility
* {*}Java 17+ Ready{*}: Fully compatible with JEP 403 strong encapsulation
* {*}Java 21 Forward Compatible{*}: No deprecation warnings or compatibility
issues
* {*}Virtual Threads Compatible{*}: Works correctly with Project Loom virtual
threads
* {*}GraalVM Native Image Ready{*}: Eliminates reflection that complicates
native image generation
h3. Developer Experience
* {*}Cleaner Code{*}: Less reflection boilerplate and error handling
* {*}Better IDE Support{*}: IDEs can analyze code without reflection
indirection
* {*}Easier Testing{*}: Tests don't require special JVM flag configurations
* {*}Reduced Maintenance{*}: Eliminates reflection-related failure modes
h3. Performance Considerations
* {*}Faster Reflection{*}: Field offset caching is more efficient than method
reflection
* {*}JIT Optimization{*}: Direct Unsafe field access optimizes better than
reflective method invocation
* {*}Reduced Overhead{*}: Eliminates {{invoke()}} overhead on every call
* {*}Better Inlining{*}: JIT compiler can inline field access more
aggressively than reflection
h2. Module System Context
This issue is part of a broader initiative to eliminate all JVM module system
violations in Apache Geode:
h3. Related Work
* {*}GEODE-10519{*}: Eliminated
{{--add-opens=java.base/java.lang=ALL-UNNAMED}} ({{{}UnsafeThreadLocal{}}})
* {*}GEODE-10520{*}: Eliminated
{{--add-exports=java.base/sun.nio.ch=ALL-UNNAMED}} ({{{}DirectBuffer{}}})
* {*}GEODE-10521{*}: This issue - Eliminate
{{--add-opens=java.base/java.nio=ALL-UNNAMED}}
({{{}AddressableMemoryManager{}}})
* {*}Future{*}:
{{--add-opens=java.base/com.sun.management.internal=ALL-UNNAMED}}
({{{}VMStats50{}}})
h3. Strategic Goal
The ultimate goal is to run Apache Geode on Java 17, and 21 without requiring
any {{-{-}add-opens{-}}} or {{-add-exports}} flags, ensuring:
* Full compliance with Java module system
* Maximum security and encapsulation
* Forward compatibility with future Java releases
* Cloud-native and container-friendly deployment
h2. Affected Versions
* All Geode versions running on Java 17 and later
* Particularly impacts Java 17+ deployments where strong encapsulation
warnings appear
h2. References
* [JEP 260: Encapsulate Most Internal APIs|https://openjdk.org/jeps/260]
* [JEP 261: Module System|https://openjdk.org/jeps/261]
* [JEP 403: Strongly Encapsulate JDK Internals|https://openjdk.org/jeps/403]
* Java Platform Module System (JPMS) specification
h2. Additional Context
h3. Why Reflection Was Used Initially
The original implementation used reflection because:
# {{DirectByteBuffer}} constructor is package-private (not accessible)
# {{DirectByteBuffer.address()}} method is package-private (not accessible)
# Public {{ByteBuffer}} API doesn't expose native memory address
# No public API exists to wrap an arbitrary memory address as a ByteBuffer
This was a pragmatic solution when Java 9 was new and the module system
implications were less understood.
h3. Off-Heap Memory Architecture
Geode's off-heap memory management is critical for large heap scenarios:
* Reduces garbage collection pressure by keeping data off-heap
* Enables multi-gigabyte caches without GC pauses
* Provides direct memory access for serialization/deserialization
* Supports memory-mapped file operations
{{AddressableMemoryManager}} is a cornerstone of this architecture, making its
security and compatibility critical.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)