reduce memory usage — same map binding for the same type and annotation set

GitOrigin-RevId: 695fc10b114200ee67f8e10914318511856f7622
This commit is contained in:
Vladimir Krivosheev
2019-05-17 11:02:08 +03:00
committed by intellij-monorepo-bot
parent d05ecab4d8
commit 3e9153596a
6 changed files with 60 additions and 42 deletions
@@ -28,6 +28,9 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Ro
computeNonDefaultConstructorInfo(beanClass)
}
// type parameters for bean binding doesn't play any role, should be the only binding for such class
override fun createCacheKey(aClass: Class<*>, type: Type) = aClass
override fun init(originalType: Type, context: BindingInitializationContext) {
val list = context.propertyCollector.collect(beanClass)
properties = list
@@ -22,6 +22,8 @@ interface NestedBinding : RootBinding {
* Binding that can read and write data of root object.
*/
interface RootBinding : Binding {
fun createCacheKey(aClass: Class<*>, type: Type = aClass) = type
fun init(originalType: Type, context: BindingInitializationContext) {
}
@@ -25,18 +25,27 @@ abstract class BindingProducer : BindingInitializationContext {
abstract fun getNestedBinding(accessor: MutableAccessor): NestedBinding
fun getRootBinding(aClass: Class<*>, type: Type = aClass): RootBinding {
val cacheKey = createCacheKey(aClass, type)
return cacheLock.read {
// create cache only under write lock
cache.get(cacheKey)
} ?: cacheLock.write {
cache.get(cacheKey)?.let {
fun getByTypeOrByClass(): RootBinding? {
var result = cache.get(type)
if (result == null && aClass !== type) {
result = cache.get(aClass)
}
return result
}
cacheLock.read {
getByTypeOrByClass()?.let {
return it
}
}
cacheLock.write {
getByTypeOrByClass()?.let {
return it
}
createRootBinding(aClass, type, cacheKey)
val binding = createRootBinding(aClass, type, cacheKey)
cache.put(cacheKey, binding)
val binding = createRootBinding(aClass, type)
cache.put(binding.createCacheKey(aClass, type), binding)
try {
binding.init(type, this)
}
@@ -48,9 +57,7 @@ abstract class BindingProducer : BindingInitializationContext {
}
}
protected open fun createCacheKey(aClass: Class<*>, type: Type) = type
protected abstract fun createRootBinding(aClass: Class<*>, type: Type, cacheKey: Type): RootBinding
protected abstract fun createRootBinding(aClass: Class<*>, type: Type): RootBinding
@Suppress("unused")
fun clearBindingCache() {
@@ -10,6 +10,7 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.function.Consumer
// CollectionBinding implements NestedBinding directly because can mutate property value directly
internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding {
private val itemBinding = createElementBindingByType(itemType, context)
@@ -43,46 +43,39 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
}
}
// type parameters for bean binding doesn't play any role, should be the only binding for such class
override fun createCacheKey(aClass: Class<*>, type: Type): Type {
when {
aClass !== type && !Collection::class.java.isAssignableFrom(aClass) && !classToRootBindingFactory.contains(aClass) -> return aClass
else -> return type
}
}
override fun createRootBinding(aClass: Class<*>, type: Type, cacheKey: Type): RootBinding {
override fun createRootBinding(aClass: Class<*>, type: Type): RootBinding {
val customFactory = classToRootBindingFactory.get(aClass)
if (customFactory != null) {
return customFactory.invoke()
}
return when {
Collection::class.java.isAssignableFrom(aClass) -> CollectionBinding(type as ParameterizedType, this)
Collection::class.java.isAssignableFrom(aClass) -> {
CollectionBinding(type as ParameterizedType, this)
}
Map::class.java.isAssignableFrom(aClass) -> {
val typeArguments = (type as ParameterizedType).actualTypeArguments
MapBinding(typeArguments[0], typeArguments[1], this)
}
aClass.isArray -> ArrayBinding(aClass.componentType, this)
aClass.isArray -> {
ArrayBinding(aClass.componentType, this)
}
aClass.isEnum -> {
@Suppress("UNCHECKED_CAST")
EnumBinding(aClass as Class<out Enum<*>>)
}
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
PolymorphicBinding(aClass)
}
aClass is Proxy -> {
throw SerializationException("$aClass class is not supported")
}
aClass == Class::class.java -> {
// Class can be supported, but it will be implemented only when will be a real use case
throw SerializationException("$aClass class is not supported")
}
else -> {
assert(cacheKey === aClass)
if (aClass.isInterface || Modifier.isAbstract(aClass.modifiers)) {
PolymorphicBinding(aClass)
}
else if (aClass is Proxy) {
throw SerializationException("$aClass class is not supported")
}
else if (aClass == Class::class.java) {
// Class can be supported, but it will be implemented only when will be a real use case
throw SerializationException("$aClass class is not supported")
}
else {
BeanBinding(aClass)
}
BeanBinding(aClass)
}
}
}
@@ -98,12 +91,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
return it(accessor)
}
// CollectionBinding implements NestedBinding directly because can mutate property value directly
return when {
Map::class.java.isAssignableFrom(aClass) -> {
val typeArguments = (type as ParameterizedType).actualTypeArguments
MapBinding(typeArguments[0], typeArguments[1], this)
}
aClass.isArray -> ArrayBinding(aClass.componentType, this)
java.lang.Number::class.java.isAssignableFrom(aClass) -> PropertyBinding(NumberAsObjectBinding())
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
@@ -6,6 +6,7 @@ import gnu.trove.THashMap
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import java.io.ByteArrayOutputStream
import java.util.*
class MapTest {
@@ -17,6 +18,22 @@ class MapTest {
return test(bean, testName, writeConfiguration)
}
@Test
fun `same map binding for the same type and annotation set`() {
val serializer = ObjectSerializer()
val bindingProducer = getBindingProducer(serializer)
@Suppress("unused")
class TestBean {
@JvmField
val a: MutableMap<String, String> = THashMap()
val b: MutableMap<String, String> = THashMap()
}
serializer.write(TestBean(), ByteArrayOutputStream())
assertThat(getBindingCount(bindingProducer)).isEqualTo(3 /* TestBean/String/Map */)
}
@Test
fun map() {
val bean = TestMapBean()