thmd opened a new issue, #4017: URL: https://github.com/apache/fory/issues/4017
### Search before asking - [x] I had searched in the [issues](https://github.com/apache/fory/issues) and found no similar issues. ### Version ``` Fory 1.6.1 and 1.7.0 (latest). Fails on Java 8, passes on Java 21. fory-core declares maven.compiler.source/target = 8, so Java 8 is a supported target. Failing JDK : 1.8.0_491-b10, HotSpot 64-Bit Server VM 25.491-b10 Passing JDK : Temurin 21.0.11+10-LTS OS : macOS 15 (Darwin 24.6.0), arm64 ``` ### Component(s) Java ### Minimal reproduce step [fory-multi-instance-repro.zip](https://github.com/user-attachments/files/31758009/fory-multi-instance-repro.zip) On Java 8, constructing a **second** `Fory` instance in the same JVM fails, once an earlier instance has serialized an object graph large enough to trigger JIT codegen. The failure comes from `ClassResolver.initialize()` while registering Fory's **own built-in types** — the string it fails to encode is the package or type name of a Fory internal class, which is ASCII by construction. A single `Fory` instance never fails, at any payload size (checked to ~3.3 MB / 150k objects), so this is not a serialization-size problem. Once it throws, every later `Fory` construction in that JVM fails the same way. The practical impact is on any application holding more than one `Fory` — two library modules that each keep a `static` instance, or one instance per classloader in a container. The second can become permanently un-constructible depending on what the first happened to serialize. The attached `fory-multi-instance-repro.zip` is a single self-contained file, no dependencies beyond `fory-core`: unzip fory-multi-instance-repro.zip && cd fory-multi-instance-repro curl -sO https://repo1.maven.org/maven2/org/apache/fory/fory-core/1.7.0/fory-core-1.7.0.jar javac -cp fory-core-1.7.0.jar ForyMultiInstanceRepro.java java -cp fory-core-1.7.0.jar:. ForyMultiInstanceRepro Optional args: `[items] [instances] [builderConfig]`, defaults `800 4 copy+num`. Some configurations are probabilistic, so run it a few times to see a rate: for i in $(seq 1 10); do java -cp fory-core-1.7.0.jar:. ForyMultiInstanceRepro; done ### What did you expect to see? OK items=800 instances=4 bytes=194341 ### What did you see instead? ``` 10 of 10 runs fail on Java 8; 0 of 10 on Java 21. FAIL at instance 2 of 4 (items=800, bytes=194341): java.lang.IllegalArgumentException: Non-ASCII characters in meta string are not allowed java.lang.IllegalArgumentException: Non-ASCII characters in meta string are not allowed at org.apache.fory.meta.MetaStringEncoder.encodeBinary(MetaStringEncoder.java:92) at org.apache.fory.resolver.SharedRegistry.getEncodedMetaString(SharedRegistry.java:337) at org.apache.fory.resolver.SharedRegistry.getPackageEncodedMetaString(SharedRegistry.java:309) at org.apache.fory.resolver.TypeInfo.<init>(TypeInfo.java:106) at org.apache.fory.resolver.ClassResolver.registerInternalImpl(ClassResolver.java:728) at org.apache.fory.resolver.ClassResolver.registerInternal(ClassResolver.java:716) at org.apache.fory.resolver.ClassResolver.initialize(ClassResolver.java:273) at org.apache.fory.Fory.<init>(Fory.java:155) at org.apache.fory.config.ForyBuilder.newFory(ForyBuilder.java:789) at org.apache.fory.ThreadLocalFory.newFory(ThreadLocalFory.java:67) Variations across runs of the same command, all with the above root cause: - it also fails via getTypeNameEncodedMetaString (SharedRegistry.java:317), so either meta string can be the one that fails; - on 1.7.0 it is sometimes surfaced wrapped, as org.apache.fory.exception.SerializationException: java.lang.RuntimeException: Create sequential serializer failed (7/10 raw, 3/10 wrapped in one sample of 10); - the failing instance index is usually 2, occasionally 3. Results, 10 runs per cell: fory items instances Java 8 Java 21 1.6.1 800 4 10/10 fail 0/10 1.7.0 800 4 10/10 fail 0/10 1.6.1 800 1 0/10 0/10 ``` ### Anything Else? Conditions, each verified by removing it: 1. Java 8. Not reproducible on Java 21, including at 8 instances and 4x the payload. 2. More than one Fory instance constructed in the JVM. 3. An earlier serialization large enough to trigger JIT codegen. Built-in types alone never trigger it. A flat HashMap<Integer, UserType> of 6000 entries does NOT reproduce; several distinct generic shapes (Map<Integer,.>, Map<Long,.>, Set<Long>) do. It is not a simple size threshold — the same shape reproduces at 2000 entries per field but not at 10000. 4. withRefTracking(true). The failure rate tracks how many builder methods are called. With withLanguage + requireClassRegistration(false) + withRefTracking(true) alone it reproduces intermittently; adding further builder calls raises the rate (8 runs each, Java 8; reproduce each row with the third arg, e.g. `... ForyMultiInstanceRepro 800 4 min`): builder config arg fail rate minimal (above) min 2/8 + withRefCopy(true) copy 2/8 + serializeEnumByName(true) enum 6/8 + withNumberCompressed(false) num 7/8 + withRefCopy(true) + withNumberCompressed(false) copy+num 8/8 serializeEnumByName(true) raises the rate although the payload contains no enums, and withRefCopy(true) affects only copy(), which is never called here. That suggests the trigger is sensitive to the number of action-recording builder methods replayed in ForyBuilder.factory(), rather than to any single option's semantics. withCodegen(false) and withAsyncCompilation(true) each reduce the rate without eliminating it, so neither is a workaround. Ruled out: - A shared SharedRegistry instance. Fory.<init>(builder, classLoader, sharedRegistry) does new SharedRegistry() when the argument is null, so separate instances do not share one. - The isLatin/ASCII range gap. StringEncodingUtils.isLatin(char[]) accepts chars <= 255 while the LOWER_SPECIAL encoders reject > 127, but encodeBinary(String, Encoding[]) correctly falls back to UTF-8 — verified with "café", "aÿ", "aĀ", "a中" on both JDKs. The gap applies only to the forced-encoding overload, which is what this path uses. - The multi-release jar. PlatformStringUtils and MemoryBuffer are overridden only under META-INF/versions/25/, so Java 8 and Java 21 both load the base classes. Possibly related: - #3828 — deserialization depending on Fory instance state (different symptom, same theme of cross-instance coupling). - #3546 — introduced caching of serializers and TypeInfo in SharedRegistry. - #2070 — a prior Java 8 vs 8+ divergence in the same internal registration path. Suggested hardening, independent of root cause: MetaStringEncoder.encodeBinary(String, Encoding) throws when the forced encoding cannot represent the string, while the Encoding[] overload falls back to UTF-8. Making the forced-encoding path fall back the same way would downgrade this from an error that permanently prevents Fory construction in the JVM to correct (if slightly larger) output. ### Are you willing to submit a PR? - [ ] I'm willing to submit a PR! -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
