This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 2670d03fa refactor(java): embed GraalVM feature in core (#3845)
2670d03fa is described below

commit 2670d03fa438d901c3667dc935482e101278d2a1
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Jul 14 22:35:02 2026 +0530

    refactor(java): embed GraalVM feature in core (#3845)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence or equivalent persisted links of the
    final clean AI review results from both fresh reviewers described in
    `AI_POLICY.md`, the Fory-guided reviewer and the independent general
    reviewer, on the current PR diff or current HEAD after the latest code
    changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 ci/release.py                                      |  86 ++++++-
 ci/run_ci.sh                                       |  13 +-
 docs/guide/java/compression.md                     |  26 ++-
 docs/guide/java/configuration.md                   |   4 +-
 docs/guide/java/graalvm-support.md                 | 143 +++++-------
 integration_tests/graalvm_tests/README.md          |   5 +-
 integration_tests/graalvm_tests/pom.xml            |  94 +++++++-
 .../graalvm_tests/src/main/java/module-info.java   |  30 +++
 .../graalvm_tests/reflect-config.json              |  98 --------
 .../apache/fory/util/GraalvmSupportRecordTest.java |  71 ------
 integration_tests/jpms_tests/run_jlink_smoke.sh    |  76 ++++--
 java/README.md                                     |  14 +-
 java/fory-core/pom.xml                             | 259 +++++++++++++++------
 .../java/org/apache/fory/config/ForyBuilder.java   |  18 +-
 .../serializer/CompressedArraySerializers.java     |  23 +-
 .../apache/fory/util/ArrayCompressionUtils.java    |  70 ++----
 .../fory/util/PrimitiveArrayCompressionType.java   |   4 +
 java/fory-core/src/main/java16/module-info.java    |   2 +
 .../apache/fory/util/ArrayCompressionUtils.java    |   7 +-
 .../apache/fory/platform}/ForyGraalVMFeature.java  |   9 +-
 java/fory-core/src/main/java25/module-info.java    |   2 +
 java/fory-core/src/main/java9/module-info.java     |   2 +
 .../fory-core/native-image.properties              |   3 +-
 .../fory/builder/GraalvmFeatureJarVerifier.java    | 181 ++++++++++++++
 .../apache/fory/platform/GraalvmSupportTest.java}  |  24 +-
 java/fory-graalvm-feature/pom.xml                  |  91 --------
 .../fory-graalvm-feature/native-image.properties   |  18 --
 .../org.graalvm.nativeimage.hosted.Feature         |   1 -
 java/fory-testsuite/pom.xml                        |  43 +---
 .../fory/serializer/ArrayCompressionTest.java      |   4 +-
 .../fory/serializer/ArrayCompressionUtilsTest.java |   4 +-
 java/pom.xml                                       |   9 -
 32 files changed, 814 insertions(+), 620 deletions(-)

diff --git a/ci/release.py b/ci/release.py
index 2e13369f4..24dd645e5 100644
--- a/ci/release.py
+++ b/ci/release.py
@@ -38,6 +38,19 @@ FORY_CORE_JDK25_ENTRY = (
     "META-INF/versions/25/org/apache/fory/reflect/InstanceFieldAccessors.class"
 )
 FORY_CORE_ACCESSOR = 
"org.apache.fory.reflect.InstanceFieldAccessors$InstanceAccessor"
+FORY_CORE_FEATURE = "org.apache.fory.platform.ForyGraalVMFeature"
+FORY_CORE_FEATURE_ENTRY = (
+    "META-INF/versions/17/org/apache/fory/platform/ForyGraalVMFeature.class"
+)
+FORY_CORE_FEATURE_SOURCE_ENTRY = (
+    "META-INF/versions/17/org/apache/fory/platform/ForyGraalVMFeature.java"
+)
+FORY_CORE_NATIVE_IMAGE_PROPERTIES = (
+    "META-INF/native-image/org.apache.fory/fory-core/native-image.properties"
+)
+GRAALVM_FEATURE_SERVICE_ENTRY = (
+    "META-INF/services/org.graalvm.nativeimage.hosted.Feature"
+)
 MAVEN_RELEASE_CMD = (
     "mvn -T10 clean deploy --no-transfer-progress -DskipTests -Papache-release"
 )
@@ -361,20 +374,75 @@ def _java_props(output):
 
 def _verify_fory_core_mr_jar():
     jar_path = _fory_core_jar_path()
+    sources_jar_path = _fory_core_jar_path("sources")
     if not os.path.exists(jar_path):
         raise FileNotFoundError(
             f"Missing fory-core release jar: {jar_path}. "
             "Run the Java release before publishing Kotlin or Scala artifacts."
         )
+    if not os.path.exists(sources_jar_path):
+        raise FileNotFoundError(
+            f"Missing fory-core release sources jar: {sources_jar_path}. "
+            "Run the Java release before publishing Kotlin or Scala artifacts."
+        )
     with zipfile.ZipFile(jar_path) as jar:
-        names = set(jar.namelist())
+        names = jar.namelist()
         manifest = jar.read("META-INF/MANIFEST.MF").decode("utf-8")
+        if names.count(FORY_CORE_NATIVE_IMAGE_PROPERTIES) != 1:
+            raise RuntimeError(
+                f"{jar_path} must contain exactly one "
+                f"{FORY_CORE_NATIVE_IMAGE_PROPERTIES}"
+            )
+        native_image_properties = 
jar.read(FORY_CORE_NATIVE_IMAGE_PROPERTIES).decode(
+            "utf-8"
+        )
     if "Multi-Release: true" not in manifest:
         raise RuntimeError(f"{jar_path} is missing manifest Multi-Release: 
true")
     if "Build-Jdk-Spec: 25" not in manifest:
         raise RuntimeError(f"{jar_path} was not built with JDK 25")
     if FORY_CORE_JDK25_ENTRY not in names:
         raise RuntimeError(f"{jar_path} is missing {FORY_CORE_JDK25_ENTRY}")
+    feature_entries = [
+        name
+        for name in names
+        if name.endswith("org/apache/fory/platform/ForyGraalVMFeature.class")
+    ]
+    if feature_entries != [FORY_CORE_FEATURE_ENTRY]:
+        raise RuntimeError(
+            f"{jar_path} must contain only the MR17 GraalVM Feature; "
+            f"found {feature_entries}"
+        )
+    feature_service_entries = [
+        name for name in names if name.endswith(GRAALVM_FEATURE_SERVICE_ENTRY)
+    ]
+    if feature_service_entries:
+        raise RuntimeError(
+            f"{jar_path} contains obsolete Feature service metadata: "
+            f"{feature_service_entries}"
+        )
+    feature_options = re.findall(r"--features=[^\s\\]+", 
native_image_properties)
+    expected_feature_option = f"--features={FORY_CORE_FEATURE}"
+    if feature_options != [expected_feature_option]:
+        raise RuntimeError(
+            f"{FORY_CORE_NATIVE_IMAGE_PROPERTIES} must contain exactly "
+            f"{expected_feature_option}; found {feature_options}"
+        )
+    if "--initialize-at-build-time=" not in native_image_properties:
+        raise RuntimeError(
+            f"{FORY_CORE_NATIVE_IMAGE_PROPERTIES} is missing 
--initialize-at-build-time"
+        )
+    with zipfile.ZipFile(sources_jar_path) as sources_jar:
+        source_names = sources_jar.namelist()
+    feature_source_entries = [
+        name
+        for name in source_names
+        if name.endswith("org/apache/fory/platform/ForyGraalVMFeature.java")
+    ]
+    if feature_source_entries != [FORY_CORE_FEATURE_SOURCE_ENTRY]:
+        raise RuntimeError(
+            f"{sources_jar_path} must contain only the MR17 GraalVM Feature 
source; "
+            f"found {feature_source_entries}"
+        )
     javap = subprocess.run(
         [
             _java_tool("javap"),
@@ -384,6 +452,7 @@ def _verify_fory_core_mr_jar():
             jar_path,
             "-p",
             FORY_CORE_ACCESSOR,
+            FORY_CORE_FEATURE,
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
@@ -394,17 +463,25 @@ def _verify_fory_core_mr_jar():
         raise RuntimeError(f"{FORY_CORE_ACCESSOR} is not the JDK25 VarHandle 
class")
     if "sun.misc.Unsafe" in javap.stdout:
         raise RuntimeError(f"{FORY_CORE_ACCESSOR} still exposes 
sun.misc.Unsafe")
-    logger.info("Verified fory-core Multi-Release JDK25 jar: %s", jar_path)
+    feature_declaration = rf"(?m)^final class {re.escape(FORY_CORE_FEATURE)}\b"
+    if not re.search(feature_declaration, javap.stdout):
+        raise RuntimeError(f"{FORY_CORE_FEATURE} must remain a non-public 
final class")
+    logger.info(
+        "Verified fory-core Multi-Release release jars: %s, %s",
+        jar_path,
+        sources_jar_path,
+    )
 
 
-def _fory_core_jar_path():
+def _fory_core_jar_path(classifier=None):
     version = _read_java_version()
+    classifier_suffix = f"-{classifier}" if classifier else ""
     return os.path.join(
         PROJECT_ROOT_DIR,
         "java",
         "fory-core",
         "target",
-        f"fory-core-{version}.jar",
+        f"fory-core-{version}{classifier_suffix}.jar",
     )
 
 
@@ -519,7 +596,6 @@ def bump_java_version(new_version):
         "java/fory-json",
         "java/fory-format",
         "java/fory-extensions",
-        "java/fory-graalvm-feature",
         "java/fory-test-core",
         "java/fory-testsuite",
         "java/fory-latest-jdk-tests",
diff --git a/ci/run_ci.sh b/ci/run_ci.sh
index 487debc22..94360bb65 100755
--- a/ci/run_ci.sh
+++ b/ci/run_ci.sh
@@ -95,10 +95,17 @@ graalvm_test() {
   mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl 
'!:fory-testsuite'
   echo "Start to build graalvm native image"
   cd "$ROOT"/integration_tests/graalvm_tests
-  mvn -DskipTests=true --no-transfer-progress -Pnative package
-  echo "Built graalvm native image"
-  echo "Start to run graalvm native image"
+  mvn -DskipTests=true --no-transfer-progress -Pnative clean package
+  echo "Built GraalVM classpath native image"
+  echo "Start to run GraalVM classpath native image"
   ./target/main
+  if [[ "$java_major" -ge 25 ]]; then
+    export JDK_JAVA_OPTIONS="$(jdk25_javac_options)"
+  fi
+  mvn -DskipTests=true --no-transfer-progress -Pnative-module clean package
+  echo "Built GraalVM module-path native image"
+  echo "Start to run GraalVM module-path native image"
+  ./target/main-module
   echo "Execute graalvm tests succeed!"
 }
 
diff --git a/docs/guide/java/compression.md b/docs/guide/java/compression.md
index 587b2c1f7..43133375c 100644
--- a/docs/guide/java/compression.md
+++ b/docs/guide/java/compression.md
@@ -49,7 +49,7 @@ If a number is of `long` type but can't be represented by 
smaller bytes mostly,
 
 ## Array Compression
 
-Fory supports SIMD-accelerated compression for primitive arrays (`int[]` and 
`long[]`) when array values can fit in smaller data types. This feature is 
available on Java 16+ and uses the Vector API for optimal performance.
+Fory can compress primitive arrays (`int[]` and `long[]`) when every value 
fits in a narrower primitive type. JDK 8 through 15 use scalar range analysis. 
On JDK 16 and later, the multi-release `fory-core` JAR automatically selects 
the Vector API implementation.
 
 ### How Array Compression Works
 
@@ -76,7 +76,13 @@ Fory fory = Fory.builder()
 CompressedArraySerializers.registerSerializers(fory);
 ```
 
-Compressed array serializers are included in `fory-core` and use the Java 16+ 
Vector API when it is available.
+Compressed array serializers are included in `fory-core`. When running on JDK 
16 or later, resolve the incubator Vector API module when starting the 
application:
+
+```bash
+java --add-modules=jdk.incubator.vector ...
+```
+
+No registration or configuration change is required when moving between the 
scalar and Vector implementations. They make the same compression decisions and 
use the same serialized format.
 
 ## String Compression
 
@@ -84,18 +90,18 @@ String compression can be enabled via 
`ForyBuilder#withStringCompressed(true)`.
 
 ## Configuration Summary
 
-| Option              | Description                                   | 
Default |
-| ------------------- | --------------------------------------------- | 
------- |
-| `compressInt`       | Enable int compression                        | `true` 
 |
-| `compressLong`      | Enable long compression                       | `true` 
 |
-| `compressIntArray`  | Enable SIMD int array compression (Java 16+)  | 
`false` |
-| `compressLongArray` | Enable SIMD long array compression (Java 16+) | 
`false` |
-| `compressString`    | Enable string compression                     | 
`false` |
+| Option              | Description                         | Default |
+| ------------------- | ----------------------------------- | ------- |
+| `compressInt`       | Enable int compression              | `true`  |
+| `compressLong`      | Enable long compression             | `true`  |
+| `compressIntArray`  | Enable int array width compression  | `false` |
+| `compressLongArray` | Enable long array width compression | `false` |
+| `compressString`    | Enable string compression           | `false` |
 
 ## Performance Considerations
 
 1. **Disable compression for numeric-heavy data**: If your data is mostly 
numbers, compression overhead may not be worth it
-2. **Array compression requires Java 16+**: Uses Vector API for SIMD 
acceleration
+2. **Array compression implementation is JDK-specific**: JDK 8 through 15 use 
scalar range analysis; JDK 16 and later automatically select the Vector API 
implementation
 3. **Long compression may not help large values**: If most longs can't fit in 
smaller representations, disable it
 4. **String compression has overhead**: Only enable if strings are highly 
compressible
 
diff --git a/docs/guide/java/configuration.md b/docs/guide/java/configuration.md
index d3a56e5e9..7c4d47c9c 100644
--- a/docs/guide/java/configuration.md
+++ b/docs/guide/java/configuration.md
@@ -28,8 +28,8 @@ This page documents all configuration options available 
through `ForyBuilder`.
 | `timeRefIgnored`                    | Whether to ignore reference tracking 
of all time types registered in `TimeSerializers` and subclasses of those types 
when ref tracking is enabled. If ignored, ref tracking of every time type can 
be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For 
example, `fory.registerSerializer(Date.class, new 
DateSerializer(fory.getConfig(), true))`. Note that enabling ref tracking 
should happen before serializer codegen of any types which c [...]
 | `compressInt`                       | Enables or disables int compression 
for smaller size.                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
 | `compressLong`                      | Enables or disables long compression 
for smaller size.                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
-| `compressIntArray`                  | Enables or disables SIMD-accelerated 
compression for int arrays when values can fit in smaller data types. Requires 
Java 16+.                                                                       
                                                                                
                                                                                
                                                                                
                 [...]
-| `compressLongArray`                 | Enables or disables SIMD-accelerated 
compression for long arrays when values can fit in smaller data types. Requires 
Java 16+.                                                                       
                                                                                
                                                                                
                                                                                
                [...]
+| `compressIntArray`                  | Enables or disables compression for 
int arrays when values are small. When `CompressedArraySerializers` is 
explicitly registered, JDK 8 through 15 use scalar width analysis and JDK 16 
and later automatically select the Vector API implementation when 
`jdk.incubator.vector` is resolved.                                             
                                                                                
                                           [...]
+| `compressLongArray`                 | Enables or disables compression for 
long arrays when values are small. When `CompressedArraySerializers` is 
explicitly registered, JDK 8 through 15 use scalar width analysis and JDK 16 
and later automatically select the Vector API implementation when 
`jdk.incubator.vector` is resolved.                                             
                                                                                
                                          [...]
 | `compressString`                    | Enables or disables string compression 
for smaller size.                                                               
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 | `classLoader`                       | The classloader is fixed per `Fory` 
instance because Fory caches class metadata. To use a different loader, create 
a new `Fory` or `ThreadSafeFory` configured with that loader, or rely on the 
thread context classloader before first class resolution.                       
                                                                                
                                                                                
                     [...]
 | `compatible`                        | Schema evolution mode. `true`: readers 
can tolerate compatible field additions, removals, and reordering. `false`: use 
only when every reader and writer always uses the same class schema and you 
want faster serialization and smaller size. When unset, compatible mode is 
enabled in both xlang and native mode. [See more](schema-evolution.md).         
                                                                                
                       [...]
diff --git a/docs/guide/java/graalvm-support.md 
b/docs/guide/java/graalvm-support.md
index 1f2503fe1..72c8467bc 100644
--- a/docs/guide/java/graalvm-support.md
+++ b/docs/guide/java/graalvm-support.md
@@ -21,66 +21,54 @@ license: |
 
 ## GraalVM Native Image
 
-GraalVM `native image` compiles Java code into native executables 
ahead-of-time, resulting in faster startup and lower memory usage. However, 
native images don't support runtime JIT compilation or reflection without 
explicit configuration.
+GraalVM Native Image compiles Java applications ahead of time. Because a 
native image cannot
+discover every reflective access or generate serializers at runtime, Fory 
prepares serializers and
+the required metadata while the image is built.
 
-Apache Fory™ works excellently with GraalVM native image by using **codegen 
instead of reflection**. All serializer code is generated at build time, 
eliminating the need for reflection configuration files in most cases.
+`fory-core` contains Fory's GraalVM Feature and activates it automatically. 
Applications do not need
+an additional Fory artifact or a `--features` option.
 
 ## How It Works
 
-Fory generates serialization code at GraalVM build time when you:
+Prepare each Fory instance during build-time class initialization:
 
-1. Create Fory as a **static** field
-2. **Register** all classes in a static initializer
-3. Call `fory.ensureSerializersCompiled()` to compile serializers
-4. Configure the class to initialize at build time via 
`native-image.properties`
+1. Store the Fory instance in a static field.
+2. Register every application class that the native executable will serialize.
+3. Call `fory.ensureSerializersCompiled()` after registration is complete.
+4. Configure the owning class for build-time initialization.
 
-**The main benefit**: You don't need to configure [reflection 
json](https://www.graalvm.org/latest/reference-manual/native-image/metadata/#specifying-reflection-metadata-in-json)
 or [serialization 
json](https://www.graalvm.org/latest/reference-manual/native-image/metadata/#serialization)
 for most serializable classes.
+The Feature uses those registrations to provide the Native Image metadata 
required by Fory,
+including metadata for private constructors, records, serializer constructors, 
and registered proxy
+shapes. Application classes still need to be registered with Fory before 
serializers are compiled.
 
-Note: Fory's `asyncCompilationEnabled` option is automatically disabled for 
GraalVM native image since runtime JIT is not supported.
+Fory disables asynchronous serializer compilation in a native image because 
runtime just-in-time
+compilation is unavailable.
 
 ## Basic Usage
 
-### Step 0: Add the GraalVM Support Dependency
-
-Add `fory-graalvm-feature` to your application dependencies when building a 
native image:
-
-```xml
-<dependency>
-  <groupId>org.apache.fory</groupId>
-  <artifactId>fory-graalvm-feature</artifactId>
-  <version>${fory.version}</version>
-</dependency>
-```
-
-This dependency already ships GraalVM feature metadata in 
`META-INF/native-image`, so adding it
-automatically enables `org.apache.fory.graalvm.feature.ForyGraalVMFeature` 
during native-image
-builds.
-
-### Step 1: Create Fory and Register Classes
+### Create Fory and Register Classes
 
 ```java
 import org.apache.fory.Fory;
 
 public class Example {
-  // Must be static field
-  static Fory fory;
+  private static final Fory FORY;
 
   static {
-    fory = Fory.builder().withXlang(false).build();
-    fory.register(MyClass.class);
-    fory.register(AnotherClass.class);
-    // Compile all serializers at build time
-    fory.ensureSerializersCompiled();
+    FORY = Fory.builder().withXlang(false).build();
+    FORY.register(MyClass.class);
+    FORY.register(AnotherClass.class);
+    FORY.ensureSerializersCompiled();
   }
 
   public static void main(String[] args) {
-    byte[] bytes = fory.serialize(new MyClass());
-    MyClass obj = (MyClass) fory.deserialize(bytes);
+    byte[] bytes = FORY.serialize(new MyClass());
+    MyClass obj = (MyClass) FORY.deserialize(bytes);
   }
 }
 ```
 
-### Step 2: Configure Build-Time Initialization
+### Configure Build-Time Initialization
 
 Create 
`resources/META-INF/native-image/your-group/your-artifact/native-image.properties`:
 
@@ -88,43 +76,37 @@ Create 
`resources/META-INF/native-image/your-group/your-artifact/native-image.pr
 Args = --initialize-at-build-time=com.example.Example
 ```
 
-## What `fory-graalvm-feature` Handles
+## Registered Classes
 
-After you add the `fory-graalvm-feature` dependency, Fory automatically 
registers the extra
-GraalVM metadata needed by advanced cases such as:
+During the native-image build, Fory automatically registers the metadata 
needed for registered
+classes, including:
 
-- **Private constructors** (classes without accessible no-arg constructor)
-- **Private inner classes/records**
-- **Dynamic proxy serialization**
+- Classes with private constructors
+- Private nested classes and records
+- Serializer constructors
+- Dynamic proxy shapes registered through `GraalvmSupport`
 
-This removes the need for manual `reflect-config.json` in most applications. 
Your own
-`native-image.properties` still only needs to configure your build-time 
initialized bootstrap
+For Fory, your application metadata only needs to configure its build-time 
initialized bootstrap
 class, for example:
 
 ```properties
 Args = --initialize-at-build-time=com.example.Example
 ```
 
-| Scenario                        | Without Feature           | With Feature   
 |
-| ------------------------------- | ------------------------- | 
--------------- |
-| Public classes with no-arg ctor | Works                     | Works          
 |
-| Private constructors            | Needs reflect-config.json | 
Auto-registered |
-| Private inner records           | Needs reflect-config.json | 
Auto-registered |
-| Dynamic proxies                 | Needs manual config       | 
Auto-registered |
-
 ### Example with Private Record
 
 ```java
+import org.apache.fory.Fory;
+
 public class Example {
-  // Private inner record - requires ForyGraalVMFeature
   private record PrivateRecord(int id, String name) {}
 
-  static Fory fory;
+  private static final Fory FORY;
 
   static {
-    fory = Fory.builder().withXlang(false).build();
-    fory.register(PrivateRecord.class);
-    fory.ensureSerializersCompiled();
+    FORY = Fory.builder().withXlang(false).build();
+    FORY.register(PrivateRecord.class);
+    FORY.ensureSerializersCompiled();
   }
 }
 ```
@@ -132,6 +114,7 @@ public class Example {
 ### Example with Dynamic Proxy
 
 ```java
+import org.apache.fory.Fory;
 import org.apache.fory.platform.GraalvmSupport;
 
 public class ProxyExample {
@@ -143,27 +126,26 @@ public class ProxyExample {
     String traceId();
   }
 
-  static Fory fory;
+  private static final Fory FORY;
 
   static {
-    fory = Fory.builder().withXlang(false).build();
-    // Register the exact interface list used by Proxy.newProxyInstance(...)
+    FORY = Fory.builder().withXlang(false).build();
     GraalvmSupport.registerProxySupport(MyService.class, Audited.class);
-    fory.ensureSerializersCompiled();
+    FORY.ensureSerializersCompiled();
   }
 }
 ```
 
 Use `registerProxySupport(MyService.class)` for a single-interface proxy. For 
proxies that implement
-multiple interfaces, pass the full interface list in the same order used to 
create the proxy. With
-`fory-graalvm-feature` on the classpath, this replaces manual 
`proxy-config.json` entries for those
-registered proxy shapes.
+multiple interfaces, pass the full interface list in the same order used to 
create the proxy. Call
+this method before `ensureSerializersCompiled()`.
 
 ## Thread-Safe Fory
 
 For multi-threaded applications, use `ThreadLocalFory`:
 
 ```java
+import java.util.List;
 import org.apache.fory.Fory;
 import org.apache.fory.ThreadLocalFory;
 import org.apache.fory.ThreadSafeFory;
@@ -171,21 +153,23 @@ import org.apache.fory.ThreadSafeFory;
 public class ThreadSafeExample {
   public record Foo(int f1, String f2, List<String> f3) {}
 
-  static ThreadSafeFory fory;
+  private static final ThreadSafeFory FORY;
 
   static {
-    fory = new ThreadLocalFory(builder -> {
-      Fory f = builder.build();
-      f.register(Foo.class);
-      f.ensureSerializersCompiled();
-      return f;
-    });
+    FORY =
+        new ThreadLocalFory(
+            builder -> {
+              Fory f = builder.build();
+              f.register(Foo.class);
+              f.ensureSerializersCompiled();
+              return f;
+            });
   }
 
   public static void main(String[] args) {
     Foo foo = new Foo(10, "abc", List.of("str1", "str2"));
-    byte[] bytes = fory.serialize(foo);
-    Foo result = (Foo) fory.deserialize(bytes);
+    byte[] bytes = FORY.serialize(foo);
+    Foo result = (Foo) FORY.deserialize(bytes);
   }
 }
 ```
@@ -200,26 +184,23 @@ If you see this error:
 Type com.example.MyClass is instantiated reflectively but was never registered
 ```
 
-**Solution**: Register the class with Fory (don't add to reflect-config.json):
+Register the class before compiling serializers:
 
 ```java
 fory.register(MyClass.class);
 fory.ensureSerializersCompiled();
 ```
 
-If the class has a private constructor, either:
-
-1. Make sure `fory-graalvm-feature` is already on the native-image classpath, 
or
-2. Create a `reflect-config.json` for that specific class
+If registration is conditional, make sure the same branch runs during 
build-time initialization.
 
 ## Framework Integration
 
 For framework developers integrating Fory:
 
-1. Provide a configuration file for users to list serializable classes
-2. Load those classes and call `fory.register(Class<?>)` for each
-3. Call `fory.ensureSerializersCompiled()` after all registrations
-4. Configure your integration class for build-time initialization
+1. Provide a configuration file for users to list serializable classes.
+2. Load those classes and call `fory.register(Class<?>)` for each.
+3. Call `fory.ensureSerializersCompiled()` after all registrations.
+4. Configure your integration class for build-time initialization.
 
 ## Benchmark
 
diff --git a/integration_tests/graalvm_tests/README.md 
b/integration_tests/graalvm_tests/README.md
index 10473f941..912bb1f1a 100644
--- a/integration_tests/graalvm_tests/README.md
+++ b/integration_tests/graalvm_tests/README.md
@@ -5,7 +5,10 @@ Examples and tests for Fory serialization in graalvm native 
image
 ## Test
 
 ```bash
-mvn -DskipTests=true -Pnative package
+mvn clean -DskipTests=true -Pnative package
+./target/main
+mvn clean -DskipTests=true -Pnative-module package
+./target/main-module
 ```
 
 ## Benchmark
diff --git a/integration_tests/graalvm_tests/pom.xml 
b/integration_tests/graalvm_tests/pom.xml
index 716ae5776..ce204a5a1 100644
--- a/integration_tests/graalvm_tests/pom.xml
+++ b/integration_tests/graalvm_tests/pom.xml
@@ -33,10 +33,14 @@
   <properties>
     <maven.compiler.source>17</maven.compiler.source>
     <maven.compiler.target>17</maven.compiler.target>
+    <maven.javadoc.skip>true</maven.javadoc.skip>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <native.maven.plugin.version>0.9.28</native.maven.plugin.version>
+    <maven-dependency-plugin.version>3.11.0</maven-dependency-plugin.version>
     <mainClass>org.apache.fory.graalvm.Main</mainClass>
+    <mainModule>org.apache.fory.graalvm.tests</mainModule>
     <imageName>main</imageName>
+    <moduleImageName>main-module</moduleImageName>
   </properties>
 
   <repositories>
@@ -58,16 +62,6 @@
       <artifactId>fory-core</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.fory</groupId>
-      <artifactId>fory-graalvm-feature</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.testng</groupId>
-      <artifactId>testng</artifactId>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <build>
@@ -150,6 +144,7 @@
           <plugin>
             <groupId>org.graalvm.buildtools</groupId>
             <artifactId>native-maven-plugin</artifactId>
+            <version>${native.maven.plugin.version}</version>
             <configuration>
               <buildArgs combine.children="append">
                 
<buildArg>-J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED</buildArg>
@@ -159,6 +154,31 @@
         </plugins>
       </build>
     </profile>
+    <profile>
+      <id>native-module-jdk25</id>
+      <activation>
+        <jdk>[25,)</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>exec-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>build-native-module</id>
+                <configuration>
+                  <arguments combine.children="append">
+                    <argument>-J--sun-misc-unsafe-memory-access=deny</argument>
+                    
<argument>--add-opens=java.base/java.lang.invoke=org.apache.fory.core</argument>
+                  </arguments>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
     <profile>
       <id>native</id>
       <dependencies>
@@ -217,5 +237,59 @@
         </plugins>
       </build>
     </profile>
+    <profile>
+      <id>native-module</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-dependency-plugin</artifactId>
+            <version>${maven-dependency-plugin.version}</version>
+            <executions>
+              <execution>
+                <id>copy-native-module-dependencies</id>
+                <phase>prepare-package</phase>
+                <goals>
+                  <goal>copy-dependencies</goal>
+                </goals>
+                <configuration>
+                  <excludeTransitive>true</excludeTransitive>
+                  <includeArtifactIds>fory-core</includeArtifactIds>
+                  <includeScope>runtime</includeScope>
+                  
<outputDirectory>${project.build.directory}/module-path</outputDirectory>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>exec-maven-plugin</artifactId>
+            <version>1.5.0</version>
+            <executions>
+              <execution>
+                <id>build-native-module</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>exec</goal>
+                </goals>
+                <configuration>
+                  <executable>native-image</executable>
+                  <arguments>
+                    <argument>--no-fallback</argument>
+                    <argument>-H:+UnlockExperimentalVMOptions</argument>
+                    <argument>-o</argument>
+                    
<argument>${project.build.directory}/${moduleImageName}</argument>
+                    <argument>--module-path</argument>
+                    
<argument>${project.build.outputDirectory}${path.separator}${project.build.directory}/module-path/fory-core-${project.version}.jar</argument>
+                    <argument>--module</argument>
+                    <argument>${mainModule}/${mainClass}</argument>
+                  </arguments>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>
diff --git a/integration_tests/graalvm_tests/src/main/java/module-info.java 
b/integration_tests/graalvm_tests/src/main/java/module-info.java
new file mode 100644
index 000000000..d3e59bb81
--- /dev/null
+++ b/integration_tests/graalvm_tests/src/main/java/module-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module org.apache.fory.graalvm.tests {
+  requires org.apache.fory.core;
+
+  // Fory-generated codecs are defined by the existing codegen class loader, 
so they access the
+  // registered test models from its unnamed module.
+  exports org.apache.fory.graalvm;
+  exports org.apache.fory.graalvm.record;
+
+  opens org.apache.fory.graalvm.record to
+      org.apache.fory.core;
+}
diff --git 
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json
 
b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json
deleted file mode 100644
index b15c7bc54..000000000
--- 
a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json
+++ /dev/null
@@ -1,98 +0,0 @@
-[
-  {
-    "name": "org.apache.fory.graalvm.Foo",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.Struct",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.ArrayExample",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.ObjectStreamExample",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.EnsureSerializerExample",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.EnsureSerializerExample$Custom",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.ProxyExample$TestInvocationHandler",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": 
"org.apache.fory.graalvm.FeatureTestExample$PrivateConstructorClass",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.FeatureTestExample$TestInvocationHandler",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.AbstractClassExample$AbstractBase",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.AbstractClassExample$ConcreteA",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.AbstractClassExample$ConcreteB",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.AbstractClassExample$Container",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.record.Foo",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.record.RecordExample$Record",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  },
-  {
-    "name": "org.apache.fory.graalvm.record.RecordExample2$Record",
-    "allDeclaredConstructors": true,
-    "allDeclaredFields": true,
-    "allDeclaredMethods": true
-  }
-]
diff --git 
a/integration_tests/graalvm_tests/src/test/java/org/apache/fory/util/GraalvmSupportRecordTest.java
 
b/integration_tests/graalvm_tests/src/test/java/org/apache/fory/util/GraalvmSupportRecordTest.java
deleted file mode 100644
index 32c05fc1f..000000000
--- 
a/integration_tests/graalvm_tests/src/test/java/org/apache/fory/util/GraalvmSupportRecordTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.fory.util;
-
-import org.apache.fory.platform.GraalvmSupport;
-import org.apache.fory.util.record.RecordUtils;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-public class GraalvmSupportRecordTest {
-
-  public record PublicRecord(int value, String name) {}
-
-  private record PrivateRecord(int value) {}
-
-  public static class ClassWithNoArgCtor {
-    public ClassWithNoArgCtor() {}
-  }
-
-  public static class ClassWithoutNoArgCtor {
-    public ClassWithoutNoArgCtor(int value) {}
-  }
-
-  @Test
-  public void testIsRecord() {
-    Assert.assertTrue(RecordUtils.isRecord(PublicRecord.class));
-    Assert.assertTrue(RecordUtils.isRecord(PrivateRecord.class));
-    Assert.assertFalse(RecordUtils.isRecord(ClassWithNoArgCtor.class));
-    Assert.assertFalse(RecordUtils.isRecord(String.class));
-  }
-
-  @Test
-  public void testIsRecordConstructorPublicAccessible() {
-    
Assert.assertTrue(GraalvmSupport.isRecordConstructorPublicAccessible(PublicRecord.class));
-    
Assert.assertFalse(GraalvmSupport.isRecordConstructorPublicAccessible(PrivateRecord.class));
-    Assert.assertFalse(
-        
GraalvmSupport.isRecordConstructorPublicAccessible(ClassWithNoArgCtor.class));
-  }
-
-  @Test
-  public void testNeedReflectionRegisterForCreation() {
-    // Public record with public constructor doesn't need reflection 
registration
-    
Assert.assertFalse(GraalvmSupport.needReflectionRegisterForCreation(PublicRecord.class));
-    // Private record needs reflection registration
-    
Assert.assertTrue(GraalvmSupport.needReflectionRegisterForCreation(PrivateRecord.class));
-    // Class with no-arg constructor doesn't need reflection registration
-    
Assert.assertFalse(GraalvmSupport.needReflectionRegisterForCreation(ClassWithNoArgCtor.class));
-    // Class without no-arg constructor uses the unsafe allocation path.
-    Assert.assertFalse(
-        
GraalvmSupport.needReflectionRegisterForCreation(ClassWithoutNoArgCtor.class));
-    // Interface doesn't need reflection registration
-    
Assert.assertFalse(GraalvmSupport.needReflectionRegisterForCreation(Runnable.class));
-  }
-}
diff --git a/integration_tests/jpms_tests/run_jlink_smoke.sh 
b/integration_tests/jpms_tests/run_jlink_smoke.sh
index a3a2bfdbf..5d177ebeb 100755
--- a/integration_tests/jpms_tests/run_jlink_smoke.sh
+++ b/integration_tests/jpms_tests/run_jlink_smoke.sh
@@ -74,24 +74,27 @@ reject_jar_entry() {
   local jar_file="$1"
   local entry="$2"
   if jar tf "$jar_file" | grep -qx "$entry"; then
-    echo "Unexpected root $entry in $jar_file" >&2
+    echo "Unexpected $entry in $jar_file" >&2
     exit 1
   fi
 }
 
 require_jar_entry "$CORE_JAR" "META-INF/versions/9/module-info.class"
 reject_jar_entry "$CORE_JAR" "module-info.class"
+require_jar_entry "$CORE_JAR" 
"org/apache/fory/serializer/CompressedArraySerializers.class"
+require_jar_entry "$CORE_JAR" 
"org/apache/fory/util/ArrayCompressionUtils.class"
+require_jar_entry "$CORE_JAR" 
"org/apache/fory/util/PrimitiveArrayCompressionType.class"
 require_jar_entry "$FORMAT_JAR" "META-INF/versions/11/module-info.class"
 reject_jar_entry "$FORMAT_JAR" "module-info.class"
+if [[ "$JAVA_MAJOR" -ge 17 ]]; then
+  jar --validate --file "$CORE_JAR"
+fi
 
-if [[ "$JAVA_MAJOR" -ge 16 ]] \
-  && jar tf "$CORE_JAR" | grep -qx "META-INF/versions/16/module-info.class"; 
then
-  require_jar_entry "$CORE_JAR" 
"META-INF/versions/16/org/apache/fory/serializer/CompressedArraySerializers.class"
+if [[ "$JAVA_MAJOR" -ge 16 ]]; then
+  require_jar_entry "$CORE_JAR" "META-INF/versions/16/module-info.class"
   require_jar_entry "$CORE_JAR" 
"META-INF/versions/16/org/apache/fory/util/ArrayCompressionUtils.class"
-  require_jar_entry "$CORE_JAR" 
"META-INF/versions/16/org/apache/fory/util/PrimitiveArrayCompressionType.class"
-  reject_jar_entry "$CORE_JAR" 
"org/apache/fory/serializer/CompressedArraySerializers.class"
-  reject_jar_entry "$CORE_JAR" 
"org/apache/fory/util/ArrayCompressionUtils.class"
-  reject_jar_entry "$CORE_JAR" 
"org/apache/fory/util/PrimitiveArrayCompressionType.class"
+  reject_jar_entry "$CORE_JAR" 
"META-INF/versions/16/org/apache/fory/serializer/CompressedArraySerializers.class"
+  reject_jar_entry "$CORE_JAR" 
"META-INF/versions/16/org/apache/fory/util/PrimitiveArrayCompressionType.class"
   jar --file "$CORE_JAR" --describe-module --release 16 | grep -q "requires 
jdk.incubator.vector static"
 fi
 
@@ -180,14 +183,59 @@ import org.apache.fory.util.ArrayCompressionUtils;
 import org.apache.fory.util.PrimitiveArrayCompressionType;
 
 public final class VectorSmoke {
+  private static void assertIntType(
+      int[] values, PrimitiveArrayCompressionType expected) {
+    PrimitiveArrayCompressionType actual =
+        ArrayCompressionUtils.determineIntCompressionType(values);
+    if (actual != expected) {
+      throw new AssertionError("Expected " + expected + " for int array, got " 
+ actual);
+    }
+  }
+
+  private static void assertLongType(
+      long[] values, PrimitiveArrayCompressionType expected) {
+    PrimitiveArrayCompressionType actual =
+        ArrayCompressionUtils.determineLongCompressionType(values);
+    if (actual != expected) {
+      throw new AssertionError("Expected " + expected + " for long array, got 
" + actual);
+    }
+  }
+
   public static void main(String[] args) throws Exception {
-    int[] values = new int[1024];
-    for (int i = 0; i < values.length; i++) {
-      values[i] = (i & 0xff) - 128;
+    // Length 513 exercises both the Vector loop and its scalar tail on 
power-of-two species.
+    int[] byteValues = new int[513];
+    int[] shortValues = new int[513];
+    int[] uncompressedInts = new int[513];
+    int[] byteTail = new int[513];
+    int[] shortTail = new int[513];
+    long[] intValues = new long[513];
+    long[] uncompressedLongs = new long[513];
+    long[] intTail = new long[513];
+    for (int i = 0; i < byteValues.length; i++) {
+      byteValues[i] = i % 2 == 0 ? Byte.MIN_VALUE : Byte.MAX_VALUE;
+      shortValues[i] = i % 2 == 0 ? Short.MIN_VALUE : Short.MAX_VALUE;
+      uncompressedInts[i] = i % 2 == 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
+      intValues[i] = i % 2 == 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
+      uncompressedLongs[i] = i % 2 == 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
     }
-    if (ArrayCompressionUtils.determineIntCompressionType(values)
-        != PrimitiveArrayCompressionType.INT_TO_BYTE) {
-      throw new AssertionError("Vector compression returned the wrong range");
+    byteTail[512] = Byte.MAX_VALUE + 1;
+    shortTail[512] = Short.MAX_VALUE + 1;
+    intTail[512] = (long) Integer.MAX_VALUE + 1;
+    assertIntType(byteValues, PrimitiveArrayCompressionType.INT_TO_BYTE);
+    assertIntType(shortValues, PrimitiveArrayCompressionType.INT_TO_SHORT);
+    assertIntType(uncompressedInts, PrimitiveArrayCompressionType.NONE);
+    assertIntType(byteTail, PrimitiveArrayCompressionType.INT_TO_SHORT);
+    assertIntType(shortTail, PrimitiveArrayCompressionType.NONE);
+    assertIntType(new int[511], PrimitiveArrayCompressionType.NONE);
+    assertLongType(intValues, PrimitiveArrayCompressionType.LONG_TO_INT);
+    assertLongType(uncompressedLongs, PrimitiveArrayCompressionType.NONE);
+    assertLongType(intTail, PrimitiveArrayCompressionType.NONE);
+    assertLongType(new long[511], PrimitiveArrayCompressionType.NONE);
+
+    String classResource =
+        
ArrayCompressionUtils.class.getResource("ArrayCompressionUtils.class").toString();
+    if (!classResource.contains("META-INF/versions/16/")) {
+      throw new AssertionError("JDK 16+ did not select the Vector 
implementation: " + classResource);
     }
     System.out.println("vector-ok");
   }
diff --git a/java/README.md b/java/README.md
index c7df3b318..f5955a51e 100644
--- a/java/README.md
+++ b/java/README.md
@@ -338,12 +338,12 @@ Foo deserializedFoo = encoder.fromRow(binaryRow);
 
 See the [Java row-format guide](../docs/guide/java/row-format.md) for more 
details.
 
-### Array Compression (Java 16+)
+### Array Compression
 
-Use SIMD-accelerated compression for integer and long arrays to reduce memory 
usage when array elements have small values:
+Use width compression for integer and long arrays to reduce serialized size 
when array elements have small values. JDK 8 through 15 use scalar range 
analysis; JDK 16 and later automatically select the Vector API implementation 
from the multi-release JAR.
 
 ```java
-import org.apache.fory.simd.*;
+import org.apache.fory.serializer.CompressedArraySerializers;
 
 Fory fory = Fory.builder().withXlang(false)
   .withIntArrayCompressed(true)
@@ -358,6 +358,12 @@ int[] data = new int[1000000];
 byte[] bytes = fory.serialize(data);
 ```
 
+On JDK 16 or later, resolve the incubator Vector API module when starting the 
application:
+
+```bash
+java --add-modules=jdk.incubator.vector ...
+```
+
 ### GraalVM Native Image
 
 Fory supports GraalVM native image through code generation, eliminating the 
need for reflection configuration. Build your native image as follows:
@@ -426,7 +432,7 @@ mvn -T16 checkstyle:check
 4. **Use native mode**: For Java-only payloads, use `withXlang(false)`. Native 
mode reduces type metadata overhead and supports more Java-native types not 
available in xlang mode
 5. **Warm Up**: Allow JIT compilation to complete before benchmarking
 6. **Register Classes**: Class registration reduces metadata overhead
-7. **Use SIMD**: Enable array compression on Java 16+ for numeric arrays
+7. **Compress numeric arrays**: Enable array compression when numeric array 
values usually fit in narrower primitive types
 
 ## Contributing
 
diff --git a/java/fory-core/pom.xml b/java/fory-core/pom.xml
index bcac6e668..9f5001980 100644
--- a/java/fory-core/pom.xml
+++ b/java/fory-core/pom.xml
@@ -41,6 +41,7 @@
     <fory.java.rootdir>${basedir}/..</fory.java.rootdir>
     
<fory.mr.classes>${project.build.directory}/multi-release-classes</fory.mr.classes>
     
<fory.jdk25.test.classes>${project.build.directory}/jdk25-test-classes</fory.jdk25.test.classes>
+    <maven.source.skip>false</maven.source.skip>
   </properties>
 
   <dependencies>
@@ -145,6 +146,20 @@
       <activation>
         <jdk>[9,)</jdk>
       </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.graalvm.sdk</groupId>
+          <artifactId>graal-sdk</artifactId>
+          <version>23.1.0</version>
+          <scope>provided</scope>
+        </dependency>
+        <dependency>
+          <groupId>org.graalvm.sdk</groupId>
+          <artifactId>nativeimage</artifactId>
+          <version>23.1.0</version>
+          <scope>provided</scope>
+        </dependency>
+      </dependencies>
       <build>
         <plugins>
           <plugin>
@@ -214,7 +229,7 @@
             <version>3.1.0</version>
             <executions>
               <execution>
-                <id>compile-java16-sources</id>
+                <id>compile-java16-classes</id>
                 <phase>process-classes</phase>
                 <goals>
                   <goal>run</goal>
@@ -223,25 +238,54 @@
                   <target>
                     <delete dir="${fory.mr.classes}/16"/>
                     <mkdir dir="${fory.mr.classes}/16"/>
-                    <pathconvert property="compile.module.path" 
refid="maven.compile.classpath"
-                                 pathsep="${path.separator}"/>
                     <javac srcdir="${project.basedir}/src/main/java16"
                            destdir="${fory.mr.classes}/16"
                            includeantruntime="false"
                            fork="true"
                            executable="${java.home}/bin/javac"
                            debug="true">
-                      <include name="**/*.java"/>
+                      <include name="org/**/*.java"/>
+                      <classpath>
+                        <path refid="maven.compile.classpath"/>
+                        <pathelement 
location="${project.build.outputDirectory}"/>
+                      </classpath>
                       <compilerarg value="--source"/>
                       <compilerarg value="16"/>
                       <compilerarg value="--target"/>
                       <compilerarg value="16"/>
+                      <compilerarg value="--add-modules"/>
+                      <compilerarg value="jdk.incubator.vector"/>
+                      <compilerarg value="-sourcepath"/>
+                      <compilerarg value=""/>
+                    </javac>
+                  </target>
+                </configuration>
+              </execution>
+              <execution>
+                <id>compile-java16-module-info</id>
+                <phase>process-classes</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+                <configuration>
+                  <target>
+                    <pathconvert property="compile.module.path" 
refid="maven.compile.classpath"
+                                 pathsep="${path.separator}"/>
+                    <javac srcdir="${project.basedir}/src/main/java16"
+                           destdir="${fory.mr.classes}/16"
+                           includeantruntime="false"
+                           fork="true"
+                           executable="${java.home}/bin/javac"
+                           debug="true">
+                      <include name="module-info.java"/>
+                      <compilerarg value="--release"/>
+                      <compilerarg value="16"/>
                       <compilerarg value="--module-path"/>
                       <compilerarg path="${compile.module.path}"/>
                       <compilerarg value="--add-modules"/>
                       <compilerarg value="jdk.incubator.vector"/>
                       <compilerarg value="--patch-module"/>
-                      <compilerarg 
value="org.apache.fory.core=${project.build.outputDirectory}"/>
+                      <compilerarg 
value="org.apache.fory.core=${project.build.outputDirectory}${path.separator}${fory.mr.classes}/16"/>
                     </javac>
                   </target>
                 </configuration>
@@ -252,9 +296,9 @@
       </build>
     </profile>
     <profile>
-      <id>jdk25-multi-release</id>
+      <id>graalvm-feature-java17</id>
       <activation>
-        <jdk>[25,)</jdk>
+        <jdk>[17,)</jdk>
       </activation>
       <build>
         <plugins>
@@ -271,6 +315,148 @@
               </execution>
             </executions>
           </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <version>3.1.0</version>
+            <executions>
+              <execution>
+                <id>compile-java17-feature</id>
+                <phase>process-classes</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+                <configuration>
+                  <target>
+                    <delete dir="${fory.mr.classes}/17"/>
+                    <mkdir dir="${fory.mr.classes}/17"/>
+                    <javac srcdir="${project.basedir}/src/main/java17"
+                           destdir="${fory.mr.classes}/17"
+                           includeantruntime="false"
+                           fork="true"
+                           executable="${java.home}/bin/javac"
+                           debug="true">
+                      <include name="org/**/*.java"/>
+                      <classpath>
+                        <path refid="maven.compile.classpath"/>
+                        <pathelement 
location="${project.build.outputDirectory}"/>
+                      </classpath>
+                      <compilerarg value="--release"/>
+                      <compilerarg value="17"/>
+                      <compilerarg value="-sourcepath"/>
+                      <compilerarg value=""/>
+                    </javac>
+                  </target>
+                </configuration>
+              </execution>
+              <execution>
+                <id>patch-multi-release-source-jar</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+                <configuration>
+                  <skip>${maven.source.skip}</skip>
+                  <target>
+                    <property
+                        name="multi.release.sources.jar"
+                        
value="${project.build.directory}/${project.build.finalName}-sources.jar"/>
+                    <available
+                        file="${multi.release.sources.jar}"
+                        property="multi.release.sources.jar.present"/>
+                    <fail
+                        unless="multi.release.sources.jar.present"
+                        message="Multi-release source patching requires 
${multi.release.sources.jar}; run with source-jar generation enabled."/>
+                    <jar destfile="${multi.release.sources.jar}" update="true">
+                      <zipfileset dir="${project.basedir}/src/main/java9" 
prefix="META-INF/versions/9">
+                        <include name="**/*.java"/>
+                      </zipfileset>
+                      <zipfileset dir="${project.basedir}/src/main/java16" 
prefix="META-INF/versions/16">
+                        <include name="**/*.java"/>
+                      </zipfileset>
+                      <zipfileset dir="${project.basedir}/src/main/java17" 
prefix="META-INF/versions/17">
+                        <include name="**/*.java"/>
+                      </zipfileset>
+                      <zipfileset dir="${project.basedir}/src/main/java25" 
prefix="META-INF/versions/25">
+                        <include name="**/*.java"/>
+                      </zipfileset>
+                    </jar>
+                    <property
+                        name="multi.release.sources.check.dir"
+                        
value="${project.build.directory}/multi-release-source-check"/>
+                    <delete dir="${multi.release.sources.check.dir}"/>
+                    <mkdir dir="${multi.release.sources.check.dir}"/>
+                    <unzip src="${multi.release.sources.jar}"
+                           dest="${multi.release.sources.check.dir}">
+                      <patternset>
+                        <include name="META-INF/versions/9/module-info.java"/>
+                        <include name="META-INF/versions/16/module-info.java"/>
+                        <include 
name="META-INF/versions/17/org/apache/fory/platform/ForyGraalVMFeature.java"/>
+                        <include name="META-INF/versions/25/module-info.java"/>
+                      </patternset>
+                    </unzip>
+                    <available
+                        
file="${multi.release.sources.check.dir}/META-INF/versions/9/module-info.java"
+                        property="java9.moduleinfo.source.present"/>
+                    <available
+                        
file="${multi.release.sources.check.dir}/META-INF/versions/16/module-info.java"
+                        property="java16.moduleinfo.source.present"/>
+                    <available
+                        
file="${multi.release.sources.check.dir}/META-INF/versions/17/org/apache/fory/platform/ForyGraalVMFeature.java"
+                        property="java17.feature.source.present"/>
+                    <available
+                        
file="${multi.release.sources.check.dir}/META-INF/versions/25/module-info.java"
+                        property="jdk25.moduleinfo.source.present"/>
+                    <fail
+                        unless="java9.moduleinfo.source.present"
+                        message="Java 9 module-info.java source is missing 
from the source jar."/>
+                    <fail
+                        unless="java16.moduleinfo.source.present"
+                        message="Java 16 module-info.java source is missing 
from the source jar."/>
+                    <fail
+                        unless="java17.feature.source.present"
+                        message="Java 17 GraalVM Feature source is missing 
from the source jar."/>
+                    <fail
+                        unless="jdk25.moduleinfo.source.present"
+                        message="JDK25 versioned module-info.java source is 
missing from the source jar."/>
+                  </target>
+                </configuration>
+              </execution>
+              <execution>
+                <id>verify-graalvm-feature-multi-release-jar</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+                <configuration>
+                  <skip>${maven.source.skip}</skip>
+                  <target>
+                    <java
+                        
classname="org.apache.fory.builder.GraalvmFeatureJarVerifier"
+                        fork="true"
+                        failonerror="true">
+                      <classpath>
+                        <pathelement 
location="${project.build.testOutputDirectory}"/>
+                        <path refid="maven.test.classpath"/>
+                      </classpath>
+                      <arg 
value="${project.build.directory}/${project.build.finalName}.jar"/>
+                      <arg 
value="${project.build.directory}/${project.build.finalName}-sources.jar"/>
+                    </java>
+                  </target>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>jdk25-multi-release</id>
+      <activation>
+        <jdk>[25,)</jdk>
+      </activation>
+      <build>
+        <plugins>
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-antrun-plugin</artifactId>
@@ -387,65 +573,6 @@
                   </target>
                 </configuration>
               </execution>
-              <execution>
-                <id>patch-multi-release-source-jar</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>run</goal>
-                </goals>
-                <configuration>
-                  <target>
-                    <property
-                        name="jdk25.sources.jar"
-                        
value="${project.build.directory}/${project.build.finalName}-sources.jar"/>
-                    <available file="${jdk25.sources.jar}" 
property="jdk25.sources.jar.present"/>
-                    <fail
-                        unless="jdk25.sources.jar.present"
-                        message="Multi-release source patching requires 
${jdk25.sources.jar}; run with source-jar generation enabled."/>
-                    <jar destfile="${jdk25.sources.jar}" update="true">
-                      <zipfileset dir="${project.basedir}/src/main/java9" 
prefix="META-INF/versions/9">
-                        <include name="**/*.java"/>
-                      </zipfileset>
-                      <zipfileset dir="${project.basedir}/src/main/java16" 
prefix="META-INF/versions/16">
-                        <include name="**/*.java"/>
-                      </zipfileset>
-                      <zipfileset dir="${project.basedir}/src/main/java25" 
prefix="META-INF/versions/25">
-                        <include name="**/*.java"/>
-                      </zipfileset>
-                    </jar>
-                    <property
-                        name="jdk25.sources.check.dir"
-                        value="${project.build.directory}/jdk25-source-check"/>
-                    <delete dir="${jdk25.sources.check.dir}"/>
-                    <mkdir dir="${jdk25.sources.check.dir}"/>
-                    <unzip src="${jdk25.sources.jar}" 
dest="${jdk25.sources.check.dir}">
-                      <patternset>
-                        <include name="META-INF/versions/9/module-info.java"/>
-                        <include name="META-INF/versions/16/module-info.java"/>
-                        <include name="META-INF/versions/25/module-info.java"/>
-                      </patternset>
-                    </unzip>
-                    <available
-                        
file="${jdk25.sources.check.dir}/META-INF/versions/9/module-info.java"
-                        property="java9.moduleinfo.source.present"/>
-                    <available
-                        
file="${jdk25.sources.check.dir}/META-INF/versions/16/module-info.java"
-                        property="java16.moduleinfo.source.present"/>
-                    <available
-                        
file="${jdk25.sources.check.dir}/META-INF/versions/25/module-info.java"
-                        property="jdk25.moduleinfo.source.present"/>
-                    <fail
-                        unless="java9.moduleinfo.source.present"
-                        message="Java 9 module-info.java source is missing 
from the source jar."/>
-                    <fail
-                        unless="java16.moduleinfo.source.present"
-                        message="Java 16 module-info.java source is missing 
from the source jar."/>
-                    <fail
-                        unless="jdk25.moduleinfo.source.present"
-                        message="JDK25 versioned module-info.java source is 
missing from the source jar."/>
-                  </target>
-                </configuration>
-              </execution>
             </executions>
           </plugin>
           <plugin>
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java 
b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
index da2962c0b..e0e1cde6d 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
@@ -234,14 +234,28 @@ public final class ForyBuilder {
     return this;
   }
 
-  /** Whether compress int arrays when values are small. */
+  /**
+   * Whether to compress int arrays when values are small.
+   *
+   * <p>When {@link org.apache.fory.serializer.CompressedArraySerializers} is 
registered, its range
+   * analysis is scalar on JDK 8 through 15. On JDK 16 and later, the 
multi-release {@code
+   * fory-core} JAR automatically selects the Vector API implementation when 
{@code
+   * jdk.incubator.vector} is resolved.
+   */
   public ForyBuilder withIntArrayCompressed(boolean intArrayCompressed) {
     this.compressIntArray = intArrayCompressed;
     recordAction(b -> b.withIntArrayCompressed(intArrayCompressed));
     return this;
   }
 
-  /** Whether compress long arrays when values are small. */
+  /**
+   * Whether to compress long arrays when values are small.
+   *
+   * <p>When {@link org.apache.fory.serializer.CompressedArraySerializers} is 
registered, its range
+   * analysis is scalar on JDK 8 through 15. On JDK 16 and later, the 
multi-release {@code
+   * fory-core} JAR automatically selects the Vector API implementation when 
{@code
+   * jdk.incubator.vector} is resolved.
+   */
   public ForyBuilder withLongArrayCompressed(boolean longArrayCompressed) {
     this.compressLongArray = longArrayCompressed;
     recordAction(b -> b.withLongArrayCompressed(longArrayCompressed));
diff --git 
a/java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java
 
b/java/fory-core/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java
similarity index 92%
rename from 
java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java
rename to 
java/fory-core/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java
index 04d4173ad..56147d6d4 100644
--- 
a/java/fory-core/src/main/java16/org/apache/fory/serializer/CompressedArraySerializers.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java
@@ -36,12 +36,19 @@ import org.apache.fory.util.ArrayCompressionUtils;
 import org.apache.fory.util.PrimitiveArrayCompressionType;
 
 /**
- * Compressed array serializers with optional Java 16+ Vector API acceleration.
+ * Compressed serializers for {@code int[]} and {@code long[]} values that fit 
in narrower primitive
+ * types.
  *
  * <p>To use these serializers, simply call {@code 
CompressedArraySerializers.register(fory)} on
  * your Fory instance. These will override the default array serializers for 
{@code int[]} and
  * {@code long[]} arrays with compressed versions that can significantly 
reduce serialization size
  * when arrays contain values that fit in smaller primitive types.
+ *
+ * <p>Fory selects the range-analysis implementation automatically. JDK 8 
through 15 use the scalar
+ * implementation, while JDK 16 and later use the Vector API implementation 
from the multi-release
+ * {@code fory-core} JAR. Applications running on JDK 16 or later must resolve 
the incubator module
+ * with {@code --add-modules=jdk.incubator.vector}. Registration and the 
serialized format are
+ * identical on every JDK.
  */
 public final class CompressedArraySerializers {
 
@@ -66,7 +73,8 @@ public final class CompressedArraySerializers {
    *
    * <pre>{@code
    * Fory fory = Fory.builder().withXlang(false)
-   *     .withConfig(Config.compressIntArray(true).compressLongArray(true))
+   *     .withIntArrayCompressed(true)
+   *     .withLongArrayCompressed(true)
    *     .build();
    * CompressedArraySerializers.registerSerializers(fory);
    * }</pre>
@@ -104,9 +112,10 @@ public final class CompressedArraySerializers {
    *
    * <pre>{@code
    * ThreadSafeFory fory = Fory.builder().withXlang(false)
-   *     .withConfig(Config.compressIntArray(true).compressLongArray(true))
+   *     .withIntArrayCompressed(true)
+   *     .withLongArrayCompressed(true)
    *     .buildThreadSafeFory();
-   * CompressedArraySerializers.registerSerializers(fory);
+   * CompressedArraySerializers.registerIfEnabled(fory);
    * }</pre>
    *
    * @param fory the ThreadSafeFory instance to register serializers with
@@ -118,9 +127,9 @@ public final class CompressedArraySerializers {
   /**
    * Register compressed array serializers with the given Fory instance.
    *
-   * <p>This will replace the default {@code int[]} and {@code long[]} 
serializers with compressed
-   * versions that use the Java 16+ Vector API for analysis and can serialize 
arrays more
-   * efficiently when values fit in smaller data types.
+   * <p>This replaces the default {@code int[]} and {@code long[]} serializers 
with compressed
+   * versions. Range analysis is scalar on JDK 8 through 15 and automatically 
uses the Vector API on
+   * JDK 16 and later when {@code jdk.incubator.vector} is resolved.
    *
    * @param fory the Fory instance to register serializers with
    */
diff --git 
a/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
 b/java/fory-core/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java
similarity index 73%
copy from 
java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
copy to 
java/fory-core/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java
index 08d2111df..1bac4c658 100644
--- 
a/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/util/ArrayCompressionUtils.java
@@ -19,13 +19,8 @@
 
 package org.apache.fory.util;
 
-import jdk.incubator.vector.IntVector;
-import jdk.incubator.vector.LongVector;
-import jdk.incubator.vector.VectorOperators;
-import jdk.incubator.vector.VectorSpecies;
-
 /**
- * Utility methods for optional primitive array compression.
+ * Utility methods for primitive-array width compression.
  *
  * <p>The compressed array serializers use these helpers when every value in a 
primitive array fits
  * in a narrower primitive type:
@@ -35,12 +30,16 @@ import jdk.incubator.vector.VectorSpecies;
  *   <li>{@code int[]} to {@code short[]} when all values are in short range.
  *   <li>{@code long[]} to {@code int[]} when all values are in int range.
  * </ul>
+ *
+ * <p>This base implementation performs scalar range analysis on JDK 8 through 
15. On JDK 16 and
+ * later, the multi-release {@code fory-core} JAR automatically replaces this 
class with the Vector
+ * API implementation; applications must resolve the incubator module with 
{@code
+ * --add-modules=jdk.incubator.vector}. Both implementations expose the same 
API, make identical
+ * compression decisions, and use the same serialized format.
  */
 public final class ArrayCompressionUtils {
   // Minimum array size to justify compression analysis and the compressed 
payload marker overhead.
   static final int MIN_COMPRESSION_SIZE = 1 << 9;
-  private static final VectorSpecies<Integer> INT_SPECIES = 
IntVector.SPECIES_PREFERRED;
-  private static final VectorSpecies<Long> LONG_SPECIES = 
LongVector.SPECIES_PREFERRED;
 
   private ArrayCompressionUtils() {}
 
@@ -61,43 +60,20 @@ public final class ArrayCompressionUtils {
     }
     boolean canCompressToByte = true;
     boolean canCompressToShort = true;
-    int i = 0;
-    int upperBound = INT_SPECIES.loopBound(array.length);
-
-    // Vector loop: test each lane against the target primitive ranges and 
stop checking a narrower
-    // representation once any lane exceeds its range.
-    for (; i < upperBound && (canCompressToByte || canCompressToShort); i += 
INT_SPECIES.length()) {
-      IntVector vector = IntVector.fromArray(INT_SPECIES, array, i);
-      if (canCompressToByte) {
-        if (vector.compare(VectorOperators.GT, Byte.MAX_VALUE).anyTrue()
-            || vector.compare(VectorOperators.LT, Byte.MIN_VALUE).anyTrue()) {
-          canCompressToByte = false;
-        }
-      }
-      if (canCompressToShort) {
-        if (vector.compare(VectorOperators.GT, Short.MAX_VALUE).anyTrue()
-            || vector.compare(VectorOperators.LT, Short.MIN_VALUE).anyTrue()) {
-          canCompressToShort = false;
-        }
-      }
-    }
-
-    // Scalar tail for elements that do not fill a complete vector.
-    for (; i < array.length && (canCompressToByte || canCompressToShort); i++) 
{
-      int value = array[i];
+    for (int value : array) {
       if (canCompressToByte && (value < Byte.MIN_VALUE || value > 
Byte.MAX_VALUE)) {
         canCompressToByte = false;
       }
       if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
         canCompressToShort = false;
       }
+      if (!canCompressToByte && !canCompressToShort) {
+        return PrimitiveArrayCompressionType.NONE;
+      }
     }
-    if (canCompressToByte) {
-      return PrimitiveArrayCompressionType.INT_TO_BYTE;
-    }
-    return canCompressToShort
-        ? PrimitiveArrayCompressionType.INT_TO_SHORT
-        : PrimitiveArrayCompressionType.NONE;
+    return canCompressToByte
+        ? PrimitiveArrayCompressionType.INT_TO_BYTE
+        : PrimitiveArrayCompressionType.INT_TO_SHORT;
   }
 
   /**
@@ -115,22 +91,8 @@ public final class ArrayCompressionUtils {
     if (array.length < MIN_COMPRESSION_SIZE) {
       return PrimitiveArrayCompressionType.NONE;
     }
-    int i = 0;
-    int upperBound = LONG_SPECIES.loopBound(array.length);
-
-    // Vector loop: any lane outside int range means long-to-int compression 
is not safe.
-    for (; i < upperBound; i += LONG_SPECIES.length()) {
-      LongVector vector = LongVector.fromArray(LONG_SPECIES, array, i);
-      if (vector.compare(VectorOperators.GT, Integer.MAX_VALUE).anyTrue()
-          || vector.compare(VectorOperators.LT, Integer.MIN_VALUE).anyTrue()) {
-        return PrimitiveArrayCompressionType.NONE;
-      }
-    }
-
-    // Scalar tail for elements that do not fill a complete vector.
-    for (; i < array.length; i++) {
-      long value = array[i];
-      if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
+    for (long value : array) {
+      if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
         return PrimitiveArrayCompressionType.NONE;
       }
     }
diff --git 
a/java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java
 
b/java/fory-core/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java
similarity index 93%
rename from 
java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java
rename to 
java/fory-core/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java
index 477b2037b..fe4849f12 100644
--- 
a/java/fory-core/src/main/java16/org/apache/fory/util/PrimitiveArrayCompressionType.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/util/PrimitiveArrayCompressionType.java
@@ -24,6 +24,10 @@ package org.apache.fory.util;
  *
  * <p>Defines the available compression strategies for reducing the size of 
primitive arrays by
  * detecting when values can be stored with a narrower primitive type.
+ *
+ * <p>Compression detection uses the scalar implementation on JDK 8 through 
15. JDK 16 and later
+ * automatically use the Vector API implementation from the multi-release 
{@code fory-core} JAR;
+ * applications must resolve {@code jdk.incubator.vector} when starting on 
those JDKs.
  */
 public enum PrimitiveArrayCompressionType {
   /** No compression applied. */
diff --git a/java/fory-core/src/main/java16/module-info.java 
b/java/fory-core/src/main/java16/module-info.java
index f9acef2cf..4236a0491 100644
--- a/java/fory-core/src/main/java16/module-info.java
+++ b/java/fory-core/src/main/java16/module-info.java
@@ -23,6 +23,8 @@ module org.apache.fory.core {
 
   requires static java.sql;
   requires static com.google.common;
+  requires static org.graalvm.nativeimage;
+  requires static org.graalvm.sdk;
   requires static org.slf4j;
   requires static jsr305;
   requires static jdk.incubator.vector;
diff --git 
a/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
 
b/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
index 08d2111df..1bb7a0f72 100644
--- 
a/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
+++ 
b/java/fory-core/src/main/java16/org/apache/fory/util/ArrayCompressionUtils.java
@@ -25,7 +25,7 @@ import jdk.incubator.vector.VectorOperators;
 import jdk.incubator.vector.VectorSpecies;
 
 /**
- * Utility methods for optional primitive array compression.
+ * Vector API implementation of primitive-array width compression for JDK 16 
and later.
  *
  * <p>The compressed array serializers use these helpers when every value in a 
primitive array fits
  * in a narrower primitive type:
@@ -35,6 +35,11 @@ import jdk.incubator.vector.VectorSpecies;
  *   <li>{@code int[]} to {@code short[]} when all values are in short range.
  *   <li>{@code long[]} to {@code int[]} when all values are in int range.
  * </ul>
+ *
+ * <p>The multi-release {@code fory-core} JAR selects this implementation 
automatically on JDK 16
+ * and later; applications must resolve the incubator module with {@code
+ * --add-modules=jdk.incubator.vector}. Earlier JDKs use the scalar base 
implementation with the
+ * same API, compression decisions, and serialized format.
  */
 public final class ArrayCompressionUtils {
   // Minimum array size to justify compression analysis and the compressed 
payload marker overhead.
diff --git 
a/java/fory-graalvm-feature/src/main/java/org/apache/fory/graalvm/feature/ForyGraalVMFeature.java
 
b/java/fory-core/src/main/java17/org/apache/fory/platform/ForyGraalVMFeature.java
similarity index 95%
rename from 
java/fory-graalvm-feature/src/main/java/org/apache/fory/graalvm/feature/ForyGraalVMFeature.java
rename to 
java/fory-core/src/main/java17/org/apache/fory/platform/ForyGraalVMFeature.java
index bf55617fa..ba0edfe15 100644
--- 
a/java/fory-graalvm-feature/src/main/java/org/apache/fory/graalvm/feature/ForyGraalVMFeature.java
+++ 
b/java/fory-core/src/main/java17/org/apache/fory/platform/ForyGraalVMFeature.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.fory.graalvm.feature;
+package org.apache.fory.platform;
 
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
@@ -26,7 +26,6 @@ import java.lang.reflect.Method;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import org.apache.fory.platform.GraalvmSupport;
 import org.apache.fory.util.record.RecordUtils;
 import org.graalvm.nativeimage.hosted.Feature;
 import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
@@ -46,11 +45,9 @@ import org.graalvm.nativeimage.hosted.RuntimeSerialization;
  *   <li>Proxy interfaces for dynamic proxy serialization
  * </ul>
  *
- * <p>Usage: Add to native-image build via 
META-INF/native-image/.../native-image.properties:
- *
- * <pre>Args = 
--features=org.apache.fory.graalvm.feature.ForyGraalVMFeature</pre>
+ * <p>The Fory core Native Image configuration activates this feature 
automatically.
  */
-public class ForyGraalVMFeature implements Feature {
+final class ForyGraalVMFeature implements Feature {
 
   private final Set<Class<?>> processedClasses = ConcurrentHashMap.newKeySet();
   private final Set<Class<?>> processedProxyInterfaces = 
ConcurrentHashMap.newKeySet();
diff --git a/java/fory-core/src/main/java25/module-info.java 
b/java/fory-core/src/main/java25/module-info.java
index 871b310ce..ce1cc3895 100644
--- a/java/fory-core/src/main/java25/module-info.java
+++ b/java/fory-core/src/main/java25/module-info.java
@@ -22,6 +22,8 @@ module org.apache.fory.core {
 
   requires static java.sql;
   requires static com.google.common;
+  requires static org.graalvm.nativeimage;
+  requires static org.graalvm.sdk;
   requires static org.slf4j;
   requires static jsr305;
   requires static jdk.incubator.vector;
diff --git a/java/fory-core/src/main/java9/module-info.java 
b/java/fory-core/src/main/java9/module-info.java
index b4a8cbf6e..f5423684b 100644
--- a/java/fory-core/src/main/java9/module-info.java
+++ b/java/fory-core/src/main/java9/module-info.java
@@ -23,6 +23,8 @@ module org.apache.fory.core {
 
   requires static java.sql;
   requires static com.google.common;
+  requires static org.graalvm.nativeimage;
+  requires static org.graalvm.sdk;
   requires static org.slf4j;
   requires static jsr305;
 
diff --git 
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
 
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
index 6cef0befe..d20a33f48 100644
--- 
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
+++ 
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
@@ -17,7 +17,8 @@
 
 # 
https://www.graalvm.org/latest/reference-manual/native-image/dynamic-features/Reflection/#unsafe-accesses
 :
 # The unsafe offset get on build time may be different from runtime
-Args=--initialize-at-build-time=org.apache.fory.annotation.ForyField$Dynamic,\
+Args=--features=org.apache.fory.platform.ForyGraalVMFeature \
+    --initialize-at-build-time=org.apache.fory.annotation.ForyField$Dynamic,\
     org.apache.fory.collection.BiMap,\
     org.apache.fory.collection.BiMap$Inverse,\
     org.apache.fory.collection.CacheBuilder$LocalCache,\
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/builder/GraalvmFeatureJarVerifier.java
 
b/java/fory-core/src/test/java/org/apache/fory/builder/GraalvmFeatureJarVerifier.java
new file mode 100644
index 000000000..5683ad4c0
--- /dev/null
+++ 
b/java/fory-core/src/test/java/org/apache/fory/builder/GraalvmFeatureJarVerifier.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fory.builder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/** Verifies the packaged GraalVM Feature and its Native Image activation 
metadata. */
+public final class GraalvmFeatureJarVerifier {
+  private static final String FEATURE_CLASS_NAME = 
"org.apache.fory.platform.ForyGraalVMFeature";
+  private static final String FEATURE_CLASS_FILE =
+      "org/apache/fory/platform/ForyGraalVMFeature.class";
+  private static final String VERSION_17_FEATURE_CLASS =
+      "META-INF/versions/17/" + FEATURE_CLASS_FILE;
+  private static final String FEATURE_SOURCE_FILE =
+      "org/apache/fory/platform/ForyGraalVMFeature.java";
+  private static final String VERSION_17_FEATURE_SOURCE =
+      "META-INF/versions/17/" + FEATURE_SOURCE_FILE;
+  private static final String NATIVE_IMAGE_PROPERTIES =
+      
"META-INF/native-image/org.apache.fory/fory-core/native-image.properties";
+  private static final String FEATURE_SERVICE =
+      "META-INF/services/org.graalvm.nativeimage.hosted.Feature";
+  private static final String FEATURE_OPTION = "--features=" + 
FEATURE_CLASS_NAME;
+  private static final String INITIALIZATION_OPTION = 
"--initialize-at-build-time=";
+
+  private GraalvmFeatureJarVerifier() {}
+
+  public static void main(String[] args) throws Exception {
+    if (args.length != 2) {
+      throw new IllegalArgumentException(
+          "Usage: GraalvmFeatureJarVerifier <fory-core.jar> 
<fory-core-sources.jar>");
+    }
+    Path jarPath = Paths.get(args[0]);
+    verifyBinaryJar(jarPath);
+    verifySourceJar(Paths.get(args[1]));
+    verifyFeatureLoading(jarPath);
+  }
+
+  private static void verifyBinaryJar(Path jarPath) throws IOException {
+    try (JarFile jarFile = new JarFile(jarPath.toFile())) {
+      Manifest manifest = jarFile.getManifest();
+      check(manifest != null, "Packaged jar has no manifest");
+      String multiRelease = 
manifest.getMainAttributes().getValue("Multi-Release");
+      check("true".equalsIgnoreCase(multiRelease), "Missing Multi-Release: 
true manifest entry");
+      check(countEntries(jarFile, FEATURE_CLASS_FILE) == 0, "Base Feature 
class must not exist");
+      check(
+          countEntriesEndingWith(jarFile, FEATURE_CLASS_FILE) == 1,
+          "Expected exactly one packaged Feature class");
+      check(
+          countEntries(jarFile, VERSION_17_FEATURE_CLASS) == 1,
+          "Expected exactly one Java 17 Feature class");
+      check(countEntries(jarFile, FEATURE_SERVICE) == 0, "Feature service file 
must not exist");
+      check(
+          countEntries(jarFile, NATIVE_IMAGE_PROPERTIES) == 1,
+          "Expected exactly one core native-image.properties");
+
+      String properties = readEntry(jarFile, NATIVE_IMAGE_PROPERTIES);
+      check(
+          countOccurrences(properties, "--features=") == 1,
+          "Expected exactly one --features option");
+      check(properties.contains(FEATURE_OPTION), "Core Feature option is 
missing");
+      check(
+          properties.contains(INITIALIZATION_OPTION),
+          "Build-time initialization option is missing");
+    }
+  }
+
+  private static void verifySourceJar(Path sourceJarPath) throws IOException {
+    try (JarFile jarFile = new JarFile(sourceJarPath.toFile())) {
+      check(countEntries(jarFile, FEATURE_SOURCE_FILE) == 0, "Base Feature 
source must not exist");
+      check(
+          countEntriesEndingWith(jarFile, FEATURE_SOURCE_FILE) == 1,
+          "Expected exactly one packaged Feature source");
+      check(
+          countEntries(jarFile, VERSION_17_FEATURE_SOURCE) == 1,
+          "Expected exactly one versioned Java 17 Feature source");
+    }
+  }
+
+  private static void verifyFeatureLoading(Path jarPath) throws Exception {
+    URL[] urls = {jarPath.toUri().toURL()};
+    try (URLClassLoader classLoader =
+        new URLClassLoader(urls, 
GraalvmFeatureJarVerifier.class.getClassLoader())) {
+      Class<?> featureClass = Class.forName(FEATURE_CLASS_NAME, true, 
classLoader);
+      check(
+          featureClass.getClassLoader() == classLoader, "Feature was not 
loaded from packaged jar");
+      check(!Modifier.isPublic(featureClass.getModifiers()), "Feature must 
remain non-public");
+      Constructor<?> constructor = featureClass.getDeclaredConstructor();
+      constructor.setAccessible(true);
+      Object feature = constructor.newInstance();
+      Method getDescription = featureClass.getMethod("getDescription");
+      getDescription.setAccessible(true);
+      Object description = getDescription.invoke(feature);
+      check(
+          description instanceof String && ((String) 
description).contains("Fory"),
+          "Packaged Feature returned an invalid description");
+    }
+  }
+
+  private static int countEntries(JarFile jarFile, String expectedName) {
+    int count = 0;
+    Enumeration<JarEntry> entries = jarFile.entries();
+    while (entries.hasMoreElements()) {
+      if (expectedName.equals(entries.nextElement().getName())) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  private static int countEntriesEndingWith(JarFile jarFile, String suffix) {
+    int count = 0;
+    Enumeration<JarEntry> entries = jarFile.entries();
+    while (entries.hasMoreElements()) {
+      if (entries.nextElement().getName().endsWith(suffix)) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  private static String readEntry(JarFile jarFile, String entryName) throws 
IOException {
+    JarEntry entry = jarFile.getJarEntry(entryName);
+    check(entry != null, "Missing jar entry " + entryName);
+    try (InputStream inputStream = jarFile.getInputStream(entry)) {
+      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+      byte[] buffer = new byte[4096];
+      int read;
+      while ((read = inputStream.read(buffer)) >= 0) {
+        outputStream.write(buffer, 0, read);
+      }
+      return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
+    }
+  }
+
+  private static int countOccurrences(String value, String target) {
+    int count = 0;
+    int offset = 0;
+    while ((offset = value.indexOf(target, offset)) >= 0) {
+      count++;
+      offset += target.length();
+    }
+    return count;
+  }
+
+  private static void check(boolean condition, String message) {
+    if (!condition) {
+      throw new AssertionError(message);
+    }
+  }
+}
diff --git 
a/java/fory-graalvm-feature/src/test/java/org/apache/fory/graalvm/feature/ForyGraalVMFeatureTest.java
 b/java/fory-core/src/test/java/org/apache/fory/platform/GraalvmSupportTest.java
similarity index 73%
rename from 
java/fory-graalvm-feature/src/test/java/org/apache/fory/graalvm/feature/ForyGraalVMFeatureTest.java
rename to 
java/fory-core/src/test/java/org/apache/fory/platform/GraalvmSupportTest.java
index 187be2177..a7d5ac4a4 100644
--- 
a/java/fory-graalvm-feature/src/test/java/org/apache/fory/graalvm/feature/ForyGraalVMFeatureTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/platform/GraalvmSupportTest.java
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-package org.apache.fory.graalvm.feature;
+package org.apache.fory.platform;
 
-import static org.testng.Assert.*;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
-import org.apache.fory.platform.GraalvmSupport;
 import org.testng.annotations.Test;
 
-public class ForyGraalVMFeatureTest {
+public class GraalvmSupportTest {
 
   public static class PublicNoArgConstructorClass {
     public PublicNoArgConstructorClass() {}
@@ -46,32 +46,16 @@ public class ForyGraalVMFeatureTest {
     VALUE
   }
 
-  @Test
-  public void testGetDescription() {
-    ForyGraalVMFeature feature = new ForyGraalVMFeature();
-    assertNotNull(feature.getDescription());
-    assertTrue(feature.getDescription().contains("Fory"));
-  }
-
   @Test
   public void testNeedReflectionRegisterForCreation() {
-    // Classes with public no-arg constructor don't need reflection 
registration
     assertFalse(
         
GraalvmSupport.needReflectionRegisterForCreation(PublicNoArgConstructorClass.class));
-
-    // Classes with private no-arg constructor need reflection registration.
     assertTrue(
         
GraalvmSupport.needReflectionRegisterForCreation(PrivateNoArgConstructorClass.class));
-
-    // Classes without no-arg constructor use the unsafe allocation path.
     
assertFalse(GraalvmSupport.needReflectionRegisterForCreation(NoNoArgConstructorClass.class));
-
-    // Abstract classes, interfaces, enums don't need reflection registration
     
assertFalse(GraalvmSupport.needReflectionRegisterForCreation(AbstractClass.class));
     
assertFalse(GraalvmSupport.needReflectionRegisterForCreation(SampleInterface.class));
     
assertFalse(GraalvmSupport.needReflectionRegisterForCreation(SampleEnum.class));
-
-    // Arrays don't need reflection registration
     
assertFalse(GraalvmSupport.needReflectionRegisterForCreation(String[].class));
   }
 }
diff --git a/java/fory-graalvm-feature/pom.xml 
b/java/fory-graalvm-feature/pom.xml
deleted file mode 100644
index 9e875b4a9..000000000
--- a/java/fory-graalvm-feature/pom.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-  <parent>
-    <artifactId>fory-parent</artifactId>
-    <groupId>org.apache.fory</groupId>
-    <version>1.4.0-SNAPSHOT</version>
-    <relativePath>../pom.xml</relativePath>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-
-  <artifactId>fory-graalvm-feature</artifactId>
-  <name>Fory GraalVM Feature</name>
-
-  <properties>
-    <maven.compiler.source>1.8</maven.compiler.source>
-    <maven.compiler.target>1.8</maven.compiler.target>
-    <fory.java.rootdir>${basedir}/..</fory.java.rootdir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.fory</groupId>
-      <artifactId>fory-core</artifactId>
-      <version>${project.version}</version>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.graalvm.sdk</groupId>
-      <artifactId>graal-sdk</artifactId>
-      <version>23.0.0</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.testng</groupId>
-      <artifactId>testng</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestEntries>
-              
<Automatic-Module-Name>org.apache.fory.graalvm.feature</Automatic-Module-Name>
-            </manifestEntries>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <forceJavacCompilerUse>true</forceJavacCompilerUse>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <failIfNoTests>false</failIfNoTests>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git 
a/java/fory-graalvm-feature/src/main/resources/META-INF/native-image/org.apache.fory/fory-graalvm-feature/native-image.properties
 
b/java/fory-graalvm-feature/src/main/resources/META-INF/native-image/org.apache.fory/fory-graalvm-feature/native-image.properties
deleted file mode 100644
index 39765881b..000000000
--- 
a/java/fory-graalvm-feature/src/main/resources/META-INF/native-image/org.apache.fory/fory-graalvm-feature/native-image.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-Args=--features=org.apache.fory.graalvm.feature.ForyGraalVMFeature
diff --git 
a/java/fory-graalvm-feature/src/main/resources/META-INF/services/org.graalvm.nativeimage.hosted.Feature
 
b/java/fory-graalvm-feature/src/main/resources/META-INF/services/org.graalvm.nativeimage.hosted.Feature
deleted file mode 100644
index 72cf9f839..000000000
--- 
a/java/fory-graalvm-feature/src/main/resources/META-INF/services/org.graalvm.nativeimage.hosted.Feature
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.fory.graalvm.feature.ForyGraalVMFeature
\ No newline at end of file
diff --git a/java/fory-testsuite/pom.xml b/java/fory-testsuite/pom.xml
index 0a8bb1c4d..beac37568 100644
--- a/java/fory-testsuite/pom.xml
+++ b/java/fory-testsuite/pom.xml
@@ -186,56 +186,17 @@
 
   <profiles>
     <profile>
-      <id>java16-compression-tests</id>
+      <id>java16-vector-tests</id>
       <activation>
         <jdk>[16,)</jdk>
       </activation>
       <build>
         <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-antrun-plugin</artifactId>
-            <version>3.1.0</version>
-            <executions>
-              <execution>
-                <id>compile-java16-compression-tests</id>
-                <phase>test-compile</phase>
-                <goals>
-                  <goal>run</goal>
-                </goals>
-                <configuration>
-                  <target>
-                    <mkdir dir="${project.build.testOutputDirectory}"/>
-                    <pathconvert property="java16.test.classpath" 
refid="maven.test.classpath"
-                                 pathsep="${path.separator}"/>
-                    <javac srcdir="${project.basedir}/src/test/java16"
-                           destdir="${project.build.testOutputDirectory}"
-                           
classpath="${project.basedir}/../fory-core/target/multi-release-classes/16${path.separator}${java16.test.classpath}"
-                           includeantruntime="false"
-                           fork="true"
-                           executable="${java.home}/bin/javac"
-                           debug="true">
-                      <include name="**/*.java"/>
-                      <compilerarg value="--source"/>
-                      <compilerarg value="16"/>
-                      <compilerarg value="--target"/>
-                      <compilerarg value="16"/>
-                      <compilerarg value="--add-modules"/>
-                      <compilerarg value="jdk.incubator.vector"/>
-                    </javac>
-                  </target>
-                </configuration>
-              </execution>
-            </executions>
-          </plugin>
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <configuration>
-              <argLine>--add-modules=jdk.incubator.vector</argLine>
-              <additionalClasspathElements>
-                
<additionalClasspathElement>${project.basedir}/../fory-core/target/multi-release-classes/16</additionalClasspathElement>
-              </additionalClasspathElements>
+              <argLine>${argLine} --add-modules=jdk.incubator.vector</argLine>
             </configuration>
           </plugin>
         </plugins>
diff --git 
a/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java
 
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
similarity index 99%
rename from 
java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java
rename to 
java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
index 4ad683bab..cb11599b5 100644
--- 
a/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionTest.java
+++ 
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
@@ -32,7 +32,7 @@ public class ArrayCompressionTest {
   @DataProvider(name = "intArrayData")
   public Object[][] intArrayData() {
     return new Object[][] {
-      // {description, array }
+      // {description, array}
       {"Empty array", new int[] {}},
       {"Small array", new int[] {1, 2, 3}},
       {"Byte range array", createByteRangeArray(1_000)},
@@ -45,7 +45,7 @@ public class ArrayCompressionTest {
   @DataProvider(name = "longArrayData")
   public Object[][] longArrayData() {
     return new Object[][] {
-      // {description, array }
+      // {description, array}
       {"Empty array", new long[] {}},
       {"Small array", new long[] {1L, 2L, 3L}},
       {"Int range array", createIntRangeLongArray(1000)},
diff --git 
a/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
 
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
similarity index 99%
rename from 
java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
rename to 
java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
index 6c3b64a0a..994b290d1 100644
--- 
a/java/fory-testsuite/src/test/java16/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
+++ 
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/ArrayCompressionUtilsTest.java
@@ -122,7 +122,7 @@ public class ArrayCompressionUtilsTest {
   }
 
   @Test
-  public void testLargeArraysWithSIMD() {
+  public void testLargeArrays() {
     Random random = new Random(42);
 
     // Test large array that compresses to bytes
@@ -154,7 +154,7 @@ public class ArrayCompressionUtilsTest {
   }
 
   @Test
-  public void testLargeLongArraysWithSIMD() {
+  public void testLargeLongArrays() {
     Random random = new Random(42);
 
     // Test large array that compresses to ints
diff --git a/java/pom.xml b/java/pom.xml
index b14222728..f75a9ae69 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -125,15 +125,6 @@
         <module>fory-testsuite</module>
       </modules>
     </profile>
-    <profile>
-      <id>jdk17-and-higher</id>
-      <activation>
-        <jdk>[17,]</jdk>
-      </activation>
-      <modules>
-        <module>fory-graalvm-feature</module>
-      </modules>
-    </profile>
     <profile>
       <id>jdk21-and-higher</id>
       <activation>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to