Skip to content

Commit 3fd96e8

Browse files
committed
Fix #1994
1 parent 50f6c6d commit 3fd96e8

File tree

6 files changed

+115
-112
lines changed

6 files changed

+115
-112
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ Versions: 3.x (for earlier see VERSION-2.x)
3232
#1955: Change the way `Module`s configure, interact with `ObjectMapper`
3333
#1973: Remove support for "default [Map] key serializer" configuration from
3434
`SerializerProvider`
35+
#1994: Limit size of `SerializerCache`, auto-flush on exceeding
3536
- Remove `MappingJsonFactory`

src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java

-2
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,7 @@ protected SerializerProvider(SerializerProvider src)
255255

256256
// and others initialized to default empty state
257257
_serializerCache = new SerializerCache();
258-
259258
_nullValueSerializer = src._nullValueSerializer;
260-
261259
_stdNullValueSerializer = src._stdNullValueSerializer;
262260
}
263261

src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java

+29-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.concurrent.ConcurrentHashMap;
55

66
import com.fasterxml.jackson.annotation.JsonFormat;
7+
78
import com.fasterxml.jackson.databind.*;
89
import com.fasterxml.jackson.databind.cfg.MapperConfig;
910
import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
@@ -20,14 +21,14 @@
2021
* ({@link com.fasterxml.jackson.databind.deser.DeserializerFactory}).
2122
*/
2223
public final class DeserializerCache
23-
implements java.io.Serializable // since 2.1 -- needs to be careful tho
24+
implements java.io.Serializable
2425
{
25-
private static final long serialVersionUID = 1L;
26+
private static final long serialVersionUID = 3L;
2627

2728
/*
28-
/**********************************************************
29+
/**********************************************************************
2930
/* Caching
30-
/**********************************************************
31+
/**********************************************************************
3132
*/
3233

3334
/**
@@ -40,42 +41,41 @@ public final class DeserializerCache
4041
* (should very quickly converge to zero after startup), let's
4142
* define a relatively low concurrency setting.
4243
*/
43-
final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers
44+
private final transient ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers
4445
= new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.75f, 4);
4546

4647
/**
4748
* During deserializer construction process we may need to keep track of partially
4849
* completed deserializers, to resolve cyclic dependencies. This is the
4950
* map used for storing deserializers before they are fully complete.
5051
*/
51-
final protected HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
52+
private final transient HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
5253
= new HashMap<JavaType, JsonDeserializer<Object>>(8);
5354

5455
/*
55-
/**********************************************************
56+
/**********************************************************************
5657
/* Life-cycle
57-
/**********************************************************
58+
/**********************************************************************
5859
*/
5960

6061
public DeserializerCache() { }
6162

6263
/*
63-
/**********************************************************
64+
/**********************************************************************
6465
/* JDK serialization handling
65-
/**********************************************************
66+
/**********************************************************************
6667
*/
6768

68-
Object writeReplace() {
69-
// instead of making this transient, just clear it:
70-
_incompleteDeserializers.clear();
71-
// TODO: clear out "cheap" cached deserializers?
72-
return this;
69+
// 11-Apr-2018, tatu: instead of clearing or such on write, keep everything transient,
70+
// recreate as empty. No point trying to revive cached instances
71+
protected Object readResolve() {
72+
return new DeserializerCache();
7373
}
74-
74+
7575
/*
76-
/**********************************************************
76+
/**********************************************************************
7777
/* Access to caching aspects
78-
/**********************************************************
78+
/**********************************************************************
7979
*/
8080

8181
/**
@@ -106,9 +106,9 @@ public void flushCachedDeserializers() {
106106
}
107107

108108
/*
109-
/**********************************************************
109+
/**********************************************************************
110110
/* General deserializer locating method
111-
/**********************************************************
111+
/**********************************************************************
112112
*/
113113

114114
/**
@@ -194,9 +194,9 @@ public boolean hasValueDeserializerFor(DeserializationContext ctxt,
194194

195195

196196
/*
197-
/**********************************************************
197+
/**********************************************************************
198198
/* Helper methods that handle cache lookups
199-
/**********************************************************
199+
/**********************************************************************
200200
*/
201201

202202
protected JsonDeserializer<Object> _findCachedDeserializer(JavaType type)
@@ -301,9 +301,9 @@ protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
301301
}
302302

303303
/*
304-
/**********************************************************
304+
/**********************************************************************
305305
/* Helper methods for actual construction of deserializers
306-
/**********************************************************
306+
/**********************************************************************
307307
*/
308308

309309
/**
@@ -532,9 +532,9 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt,
532532
}
533533

534534
/*
535-
/**********************************************************
535+
/**********************************************************************
536536
/* Helper methods, other
537-
/**********************************************************
537+
/**********************************************************************
538538
*/
539539

540540
/**
@@ -579,9 +579,9 @@ private Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClas
579579
}
580580

581581
/*
582-
/**********************************************************
583-
/* Overridable error reporting methods
584-
/**********************************************************
582+
/**********************************************************************
583+
/* Error reporting methods
584+
/**********************************************************************
585585
*/
586586

587587
protected JsonDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)

src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java

+38-30
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.fasterxml.jackson.databind.ser;
22

3-
import java.util.*;
43
import java.util.concurrent.atomic.AtomicReference;
54

65
import com.fasterxml.jackson.databind.*;
76
import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap;
7+
import com.fasterxml.jackson.databind.util.SimpleLookupCache;
88
import com.fasterxml.jackson.databind.util.TypeKey;
99

1010
/**
@@ -26,26 +26,46 @@ public final class SerializerCache
2626
private static final long serialVersionUID = 3L;
2727

2828
/**
29-
* Shared, modifiable map; all access needs to be through synchronized blocks.
29+
* By default, allow caching of up to 4000 serializer entries (for possibly up to
30+
* 1000 types; but depending access patterns may be as few as half of that).
31+
*/
32+
public final static int DEFAULT_MAX_CACHED = 4000;
33+
34+
/**
35+
* Shared, modifiable map; used if local read-only copy does not contain serializer
36+
* caller expects.
3037
*<p>
3138
* NOTE: keys are of various types (see below for key types), in addition to
3239
* basic {@link JavaType} used for "untyped" serializers.
3340
*/
34-
private final transient HashMap<TypeKey, JsonSerializer<Object>> _sharedMap;
41+
private final SimpleLookupCache<TypeKey, JsonSerializer<Object>> _sharedMap;
3542

3643
/**
3744
* Most recent read-only instance, created from _sharedMap, if any.
3845
*/
3946
private final transient AtomicReference<ReadOnlyClassToSerializerMap> _readOnlyMap;
4047

4148
public SerializerCache() {
42-
_sharedMap = new HashMap<TypeKey, JsonSerializer<Object>>(64);
49+
this(DEFAULT_MAX_CACHED);
50+
}
51+
52+
/**
53+
* @since 3.0
54+
*/
55+
public SerializerCache(int maxCached) {
56+
_sharedMap = new SimpleLookupCache<TypeKey, JsonSerializer<Object>>(maxCached>>2, maxCached);
4357
_readOnlyMap = new AtomicReference<ReadOnlyClassToSerializerMap>();
4458
}
4559

46-
// Since 3.0, needed to initialize cache properly
60+
private SerializerCache(SerializerCache serialized) {
61+
_sharedMap = serialized._sharedMap;
62+
_readOnlyMap = new AtomicReference<ReadOnlyClassToSerializerMap>();
63+
}
64+
65+
// Since 3.0, needed to initialize cache properly: shared map would be ok but need to
66+
// reconstruct AtomicReference
4767
protected Object readResolve() {
48-
return new SerializerCache();
68+
return new SerializerCache(this);
4969
}
5070

5171
/**
@@ -78,7 +98,7 @@ private final synchronized ReadOnlyClassToSerializerMap _makeReadOnlyLookupMap()
7898
/**********************************************************************
7999
*/
80100

81-
public synchronized int size() {
101+
public int size() {
82102
return _sharedMap.size();
83103
}
84104

@@ -88,30 +108,22 @@ public synchronized int size() {
88108
*/
89109
public JsonSerializer<Object> untypedValueSerializer(Class<?> type)
90110
{
91-
synchronized (this) {
92-
return _sharedMap.get(new TypeKey(type, false));
93-
}
111+
return _sharedMap.get(new TypeKey(type, false));
94112
}
95113

96114
public JsonSerializer<Object> untypedValueSerializer(JavaType type)
97115
{
98-
synchronized (this) {
99-
return _sharedMap.get(new TypeKey(type, false));
100-
}
116+
return _sharedMap.get(new TypeKey(type, false));
101117
}
102118

103119
public JsonSerializer<Object> typedValueSerializer(JavaType type)
104120
{
105-
synchronized (this) {
106-
return _sharedMap.get(new TypeKey(type, true));
107-
}
121+
return _sharedMap.get(new TypeKey(type, true));
108122
}
109123

110124
public JsonSerializer<Object> typedValueSerializer(Class<?> cls)
111125
{
112-
synchronized (this) {
113-
return _sharedMap.get(new TypeKey(cls, true));
114-
}
126+
return _sharedMap.get(new TypeKey(cls, true));
115127
}
116128

117129
/*
@@ -127,24 +139,20 @@ public JsonSerializer<Object> typedValueSerializer(Class<?> cls)
127139
*/
128140
public void addTypedSerializer(JavaType type, JsonSerializer<Object> ser)
129141
{
130-
synchronized (this) {
131-
if (_sharedMap.put(new TypeKey(type, true), ser) == null) {
132-
// let's invalidate the read-only copy, too, to get it updated
133-
_readOnlyMap.set(null);
134-
}
142+
if (_sharedMap.put(new TypeKey(type, true), ser) == null) {
143+
// let's invalidate the read-only copy, too, to get it updated
144+
_readOnlyMap.set(null);
135145
}
136146
}
137147

138148
public void addTypedSerializer(Class<?> cls, JsonSerializer<Object> ser)
139149
{
140-
synchronized (this) {
141-
if (_sharedMap.put(new TypeKey(cls, true), ser) == null) {
142-
// let's invalidate the read-only copy, too, to get it updated
143-
_readOnlyMap.set(null);
144-
}
150+
if (_sharedMap.put(new TypeKey(cls, true), ser) == null) {
151+
// let's invalidate the read-only copy, too, to get it updated
152+
_readOnlyMap.set(null);
145153
}
146154
}
147-
155+
148156
public void addAndResolveNonTypedSerializer(Class<?> type, JsonSerializer<Object> ser,
149157
SerializerProvider provider)
150158
throws JsonMappingException

src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java

+14-21
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
package com.fasterxml.jackson.databind.ser.impl;
22

3-
import java.util.*;
4-
53
import com.fasterxml.jackson.databind.JavaType;
64
import com.fasterxml.jackson.databind.JsonSerializer;
5+
import com.fasterxml.jackson.databind.util.SimpleLookupCache;
76
import com.fasterxml.jackson.databind.util.TypeKey;
87

98
/**
109
* Optimized lookup table for accessing two types of serializers; typed
1110
* and non-typed. Only accessed from a single thread, so no synchronization
1211
* needed for accessors.
13-
*<p>
14-
* Note that before 2.6 this class was much smaller, and referred most
15-
* operations to separate <code>JsonSerializerMap</code>, but in 2.6
16-
* functions were combined.
1712
*/
1813
public final class ReadOnlyClassToSerializerMap
1914
{
@@ -23,20 +18,18 @@ public final class ReadOnlyClassToSerializerMap
2318

2419
private final int _mask;
2520

26-
public ReadOnlyClassToSerializerMap(Map<TypeKey,JsonSerializer<Object>> serializers)
21+
protected ReadOnlyClassToSerializerMap(SimpleLookupCache<TypeKey, JsonSerializer<Object>> src)
2722
{
28-
int size = findSize(serializers.size());
29-
_size = size;
30-
_mask = (size-1);
31-
Bucket[] buckets = new Bucket[size];
32-
for (Map.Entry<TypeKey,JsonSerializer<Object>> entry : serializers.entrySet()) {
33-
TypeKey key = entry.getKey();
23+
_size = findSize(src.size());;
24+
_mask = (_size-1);
25+
Bucket[] buckets = new Bucket[_size];
26+
src.contents((key, value) -> {
3427
int index = key.hashCode() & _mask;
35-
buckets[index] = new Bucket(buckets[index], key, entry.getValue());
36-
}
28+
buckets[index] = new Bucket(buckets[index], key, value);
29+
});
3730
_buckets = buckets;
3831
}
39-
32+
4033
private final static int findSize(int size)
4134
{
4235
// For small enough results (64 or less), we'll require <= 50% fill rate; otherwise 80%
@@ -51,14 +44,14 @@ private final static int findSize(int size)
5144
/**
5245
* Factory method for constructing an instance.
5346
*/
54-
public static ReadOnlyClassToSerializerMap from(HashMap<TypeKey, JsonSerializer<Object>> src) {
47+
public static ReadOnlyClassToSerializerMap from(SimpleLookupCache<TypeKey, JsonSerializer<Object>> src) {
5548
return new ReadOnlyClassToSerializerMap(src);
5649
}
5750

5851
/*
59-
/**********************************************************
52+
/**********************************************************************
6053
/* Public API
61-
/**********************************************************
54+
/**********************************************************************
6255
*/
6356

6457
public int size() { return _size; }
@@ -132,9 +125,9 @@ public JsonSerializer<Object> untypedValueSerializer(Class<?> type)
132125
}
133126

134127
/*
135-
/**********************************************************
128+
/**********************************************************************
136129
/* Helper classes
137-
/**********************************************************
130+
/**********************************************************************
138131
*/
139132

140133
private final static class Bucket

0 commit comments

Comments
 (0)