Skip to content

Commit e4bb9fc

Browse files
Elin-ZhoueLin
authored andcommitted
fix: cache ObjectWriter created in switch block of getObjectWriterInternal to prevent Metaspace leak, for issue #7626
1 parent 3697c2d commit e4bb9fc

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterProvider.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,16 @@ private ObjectWriter getObjectWriterInternal(Type objectType, Class objectClass,
708708
break;
709709
}
710710

711+
if (objectWriter != null) {
712+
ObjectWriter previous = fieldBased
713+
? cacheFieldBased.putIfAbsent(objectType, objectWriter)
714+
: cache.putIfAbsent(objectType, objectWriter);
715+
if (previous != null) {
716+
objectWriter = previous;
717+
}
718+
return objectWriter;
719+
}
720+
711721
if (objectWriter == null
712722
&& (!fieldBased)
713723
&& Map.class.isAssignableFrom(objectClass)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.alibaba.fastjson2.writer;
2+
3+
import com.alibaba.fastjson2.JSON;
4+
import com.google.common.collect.ArrayListMultimap;
5+
import com.google.common.collect.HashMultimap;
6+
import com.google.common.collect.LinkedHashMultimap;
7+
import com.google.common.collect.LinkedListMultimap;
8+
import com.google.common.collect.TreeMultimap;
9+
import org.junit.jupiter.api.Tag;
10+
import org.junit.jupiter.api.Test;
11+
12+
import java.lang.management.ManagementFactory;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
16+
import static org.junit.jupiter.api.Assertions.assertSame;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
@Tag("writer")
20+
public class GuavaMultimapWriterCacheTest {
21+
@Test
22+
public void testLinkedListMultimapWriterCached() {
23+
ObjectWriterProvider provider = new ObjectWriterProvider();
24+
ObjectWriter w1 = provider.getObjectWriter(LinkedListMultimap.class);
25+
ObjectWriter w2 = provider.getObjectWriter(LinkedListMultimap.class);
26+
assertNotNull(w1);
27+
assertSame(w1, w2);
28+
}
29+
30+
@Test
31+
public void testArrayListMultimapWriterCached() {
32+
ObjectWriterProvider provider = new ObjectWriterProvider();
33+
ObjectWriter w1 = provider.getObjectWriter(ArrayListMultimap.class);
34+
ObjectWriter w2 = provider.getObjectWriter(ArrayListMultimap.class);
35+
assertNotNull(w1);
36+
assertSame(w1, w2);
37+
}
38+
39+
@Test
40+
public void testHashMultimapWriterCached() {
41+
ObjectWriterProvider provider = new ObjectWriterProvider();
42+
ObjectWriter w1 = provider.getObjectWriter(HashMultimap.class);
43+
ObjectWriter w2 = provider.getObjectWriter(HashMultimap.class);
44+
assertNotNull(w1);
45+
assertSame(w1, w2);
46+
}
47+
48+
@Test
49+
public void testLinkedHashMultimapWriterCached() {
50+
ObjectWriterProvider provider = new ObjectWriterProvider();
51+
ObjectWriter w1 = provider.getObjectWriter(LinkedHashMultimap.class);
52+
ObjectWriter w2 = provider.getObjectWriter(LinkedHashMultimap.class);
53+
assertNotNull(w1);
54+
assertSame(w1, w2);
55+
}
56+
57+
@Test
58+
public void testTreeMultimapWriterCached() {
59+
ObjectWriterProvider provider = new ObjectWriterProvider();
60+
ObjectWriter w1 = provider.getObjectWriter(TreeMultimap.class);
61+
ObjectWriter w2 = provider.getObjectWriter(TreeMultimap.class);
62+
assertNotNull(w1);
63+
assertSame(w1, w2);
64+
}
65+
66+
@Test
67+
public void testLinkedListMultimapSerialize() {
68+
LinkedListMultimap<String, String> map = LinkedListMultimap.create();
69+
map.put("a", "1");
70+
map.put("a", "2");
71+
map.put("b", "3");
72+
String json = JSON.toJSONString(map);
73+
assertEquals("{\"a\":[\"1\",\"2\"],\"b\":[\"3\"]}", json);
74+
75+
String json2 = JSON.toJSONString(map);
76+
assertEquals(json, json2);
77+
}
78+
79+
@Test
80+
public void testArrayListMultimapSerialize() {
81+
ArrayListMultimap<String, Integer> map = ArrayListMultimap.create();
82+
map.put("k1", 1);
83+
map.put("k1", 2);
84+
String json = JSON.toJSONString(map);
85+
assertEquals("{\"k1\":[1,2]}", json);
86+
87+
String json2 = JSON.toJSONString(map);
88+
assertEquals(json, json2);
89+
}
90+
91+
@Test
92+
public void testHashMultimapSerialize() {
93+
HashMultimap<String, String> map = HashMultimap.create();
94+
map.put("k", "v");
95+
String json = JSON.toJSONString(map);
96+
assertEquals("{\"k\":[\"v\"]}", json);
97+
}
98+
99+
@Test
100+
public void testLinkedHashMultimapSerialize() {
101+
LinkedHashMultimap<String, String> map = LinkedHashMultimap.create();
102+
map.put("a", "1");
103+
map.put("a", "2");
104+
map.put("b", "3");
105+
String json = JSON.toJSONString(map);
106+
assertEquals("{\"a\":[\"1\",\"2\"],\"b\":[\"3\"]}", json);
107+
}
108+
109+
@Test
110+
public void testTreeMultimapSerialize() {
111+
TreeMultimap<String, String> map = TreeMultimap.create();
112+
map.put("b", "2");
113+
map.put("a", "1");
114+
String json = JSON.toJSONString(map);
115+
assertEquals("{\"a\":[\"1\"],\"b\":[\"2\"]}", json);
116+
}
117+
118+
@Test
119+
public void testNoNewClassesAfterWarmup() {
120+
LinkedListMultimap<String, String> map = LinkedListMultimap.create();
121+
map.put("k", "v");
122+
123+
for (int i = 0; i < 10; i++) {
124+
JSON.toJSONString(map);
125+
}
126+
127+
long before = ManagementFactory.getClassLoadingMXBean().getTotalLoadedClassCount();
128+
for (int i = 0; i < 100; i++) {
129+
JSON.toJSONString(map);
130+
}
131+
long after = ManagementFactory.getClassLoadingMXBean().getTotalLoadedClassCount();
132+
133+
assertTrue(after - before <= 5,
134+
"Should not generate new classes after warmup, but loaded " + (after - before));
135+
}
136+
}

0 commit comments

Comments
 (0)