Repository: alibaba/fastjson2 (java)

Issue:
FastJSON2 throws OutOfMemoryError for size limits causing thread termination in async contexts
### Please describe your need or improvement suggestion

FastJSON2 throws an `OutOfMemoryError` when serializing objects exceeding the default size limit (64MB), instead of throwing a more appropriate Runtime exception. This behavior causes serious problems in applications using asynchronous threads, as `Error` instances abruptly terminate threads without any recovery possibility.

In our application, this has caused the thread from Logback to terminate unexpectedly, resulting in our application silently stopping log transmission without any recovery mechanism or warning.

#### Reproduction steps

```java
Map<String, Object> largeMap = new HashMap<>();

// Create a large string (about 1MB)
StringBuilder largeString = new StringBuilder(1024 * 1024);
for (int i = 0; i < 1024 * 1024; i++) {
    largeString.append('a');
}
String baseValue = largeString.toString();

// Create a nested structure exceeding the 64MB limit
for (int i = 0; i < 70; i++) {
    Map<String, Object> nestedMap = new HashMap<>();
    for (int j = 0; j < 10; j++) {
        nestedMap.put("value" + j, baseValue);
    }
    largeMap.put("entry" + i, nestedMap);
}

// This throws OutOfMemoryError
JSON.toJSONString(largeMap);
```

#### Stack trace

```
Exception in thread "AsyncAppender-Worker" java.lang.OutOfMemoryError: try enabling LargeObject feature instead
at com.alibaba.fastjson2.JSONWriter.newCapacity(JSONWriter.java:2701)
at com.alibaba.fastjson2.JSONWriterUTF16.grow0(JSONWriterUTF16.java:1739)
at com.alibaba.fastjson2.JSONWriterUTF16.grow(JSONWriterUTF16.java:1734)
at com.alibaba.fastjson2.JSONWriterUTF16.writeStringLatin1(JSONWriterUTF16.java:307)
...
```

### Please describe the solution you suggest

Replace:

```java
throw new OutOfMemoryError("try enabling LargeObject feature instead");
```

With a specific runtime exception like `IndexOutOfBoundsException`:

```java
throw new IndexOutOfBoundsException("Object exceeds maximum size limit of " + maxArraySize + " bytes. Try enabling LargeObject feature.");
```

This solution would allow:
1. Providing meaningful feedback
2. Represents programming errors that could be prevented (like accessing an array beyond its bounds)

### Additional information

This issue is particularly serious in contexts like asynchronous logging frameworks (such as Logback with AsyncAppender), where an error in the processing thread causes a silent failure of the entire logging infrastructure.

The current error message is helpful because it suggests the solution (enabling LargeObject), but the error type chosen is not appropriate for a configurable library limitation.

Related with #966

Pull request:
Add JSONLargeObjectException for handling large object serialization errors
Introduce JSONLargeObjectException to provide clear error messages when the maximum array size is exceeded during JSON serialization. The exception message includes the requested size and the maximum allowed size, and suggests enabling the LargeObject feature for larger payloads.

Closes #3588.

Patch:
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONLargeObjectException.java b/core/src/main/java/com/alibaba/fastjson2/JSONLargeObjectException.java
new file mode 100644
index 000000000..e374266a6
--- /dev/null
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONLargeObjectException.java
@@ -0,0 +1,17 @@
+package com.alibaba.fastjson2;
+
+/**
+ * Exception thrown when attempting to serialize an object that exceeds size limits
+ * and the LargeObject feature is not enabled.
+ *
+ * @since 2.0.58
+ */
+public class JSONLargeObjectException
+        extends JSONException {
+    public JSONLargeObjectException(String message) {
+        super(message);
+    }
+    public JSONLargeObjectException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
index f4316bf59..06fff5264 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
@@ -2728,7 +2728,8 @@ public abstract class JSONWriter
             if (minCapacity < maxArraySize) {
                 newCapacity = maxArraySize;
             } else {
-                throw new OutOfMemoryError("try enabling LargeObject feature instead");
+                throw new JSONLargeObjectException("Maximum array size exceeded. Try enabling LargeObject feature instead. "
+                        + "Requested size: " + minCapacity + ", max size: " + maxArraySize);
             }
         }
         return newCapacity;


Test patch:
diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_2300/Issue2323.java b/core/src/test/java/com/alibaba/fastjson2/issues_2300/Issue2323.java
index 2f04edf20..fa45e7c7f 100644
--- a/core/src/test/java/com/alibaba/fastjson2/issues_2300/Issue2323.java
+++ b/core/src/test/java/com/alibaba/fastjson2/issues_2300/Issue2323.java
@@ -2,6 +2,7 @@ package com.alibaba.fastjson2.issues_2300;
 
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONLargeObjectException;
 import com.alibaba.fastjson2.JSONWriter;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -12,7 +13,7 @@ import java.util.List;
 import java.util.Map;
 
 public class Issue2323 {
-    String errMsg = "try enabling LargeObject feature instead";
+    String errMsgStart = "Maximum array size exceeded. Try enabling LargeObject feature instead. Requested size: ";
 
     @Test
     public void test() throws Exception {
@@ -32,20 +33,23 @@ public class Issue2323 {
 
         try {
             JSONWriter.ofUTF16().write(params);
-        } catch (OutOfMemoryError error) {
-            Assertions.assertEquals(errMsg, error.getMessage());
+        } catch (JSONLargeObjectException error) {
+            Assertions.assertTrue(error.getMessage().startsWith(errMsgStart),
+                    "Error message should start with: " + errMsgStart + ", but was: " + error.getMessage());
         }
 
         try {
             JSONWriter.ofUTF8().write(params);
-        } catch (OutOfMemoryError error) {
-            Assertions.assertEquals(errMsg, error.getMessage());
+        } catch (JSONLargeObjectException error) {
+            Assertions.assertTrue(error.getMessage().startsWith(errMsgStart),
+                    "Error message should start with: " + errMsgStart + ", but was: " + error.getMessage());
         }
 
         try {
             JSONWriter.ofJSONB().write(params);
-        } catch (OutOfMemoryError error) {
-            Assertions.assertEquals(errMsg, error.getMessage());
+        } catch (JSONLargeObjectException error) {
+            Assertions.assertTrue(error.getMessage().startsWith(errMsgStart),
+                    "Error message should start with: " + errMsgStart + ", but was: " + error.getMessage());
         }
     }
 }