Introduce reflection cache in KClassImpl and KPackageImpl

Most of KClassImpl operations should be cached, but creating a lazy value for
each operation would significantly increase memory footprint of a KClassImpl
instance. Therefore we decide to store all lazy values under a single lazy
value named 'data' which is stored in KClassImpl. There's a minor overhead of
indirection on any operation now, however it'll allow us to substantially
increase reflection performance by caching everything we can
This commit is contained in:
Alexander Udalov
2016-08-17 15:56:08 +03:00
parent f6b471ac01
commit b9067e6462
4 changed files with 119 additions and 64 deletions
@@ -35,18 +35,36 @@ import kotlin.reflect.*
internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
KDeclarationContainerImpl(), KClass<T>, KClassifierImpl, KAnnotatedElementImpl {
private val descriptor_ = ReflectProperties.lazySoft {
val classId = classId
private inner class Data : KDeclarationContainerImpl.Data() {
val descriptor: ClassDescriptor by ReflectProperties.lazySoft {
val classId = classId
val moduleData = data().moduleData
val descriptor =
if (classId.isLocal) moduleData.deserialization.deserializeClass(classId)
else moduleData.module.findClassAcrossModuleDependencies(classId)
val descriptor =
if (classId.isLocal) moduleData.deserialization.deserializeClass(classId)
else moduleData.module.findClassAcrossModuleDependencies(classId)
descriptor ?: reportUnresolvedClass()
descriptor ?: reportUnresolvedClass()
}
@Suppress("UNCHECKED_CAST")
val objectInstance: T? by ReflectProperties.lazy {
val descriptor = descriptor
if (descriptor.kind != ClassKind.OBJECT) return@lazy null
val field = if (descriptor.isCompanionObject && !CompanionObjectMapping.isMappedIntrinsicCompanionObject(descriptor)) {
jClass.enclosingClass.getDeclaredField(descriptor.name.asString())
}
else {
jClass.getDeclaredField(JvmAbi.INSTANCE_FIELD)
}
field.get(null) as T
}
}
override val descriptor: ClassDescriptor
get() = descriptor_()
private val data = ReflectProperties.lazy { Data() }
override val descriptor: ClassDescriptor get() = data().descriptor
override val annotated: Annotated get() = descriptor
@@ -70,7 +88,6 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
return emptyList()
}
@Suppress("UNCHECKED_CAST")
override fun getProperties(name: Name): Collection<PropertyDescriptor> =
(memberScope.getContributedVariables(name, NoLookupLocation.FROM_REFLECTION) +
staticScope.getContributedVariables(name, NoLookupLocation.FROM_REFLECTION))
@@ -123,22 +140,7 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
jClass?.let { KClassImpl(it) }
}
@Suppress("UNCHECKED_CAST")
private val objectInstance_ = ReflectProperties.lazy {
val descriptor = descriptor
if (descriptor.kind != ClassKind.OBJECT) return@lazy null
val field = if (descriptor.isCompanionObject && !CompanionObjectMapping.isMappedIntrinsicCompanionObject(descriptor)) {
jClass.enclosingClass.getDeclaredField(descriptor.name.asString())
}
else {
jClass.getDeclaredField(JvmAbi.INSTANCE_FIELD)
}
field.get(null) as T
}
override val objectInstance: T?
get() = objectInstance_()
override val objectInstance: T? get() = data().objectInstance
override fun isInstance(value: Any?): Boolean {
// TODO: use Kotlin semantics for mutable/read-only collections once KT-11754 is supported (see TypeIntrinsics)
@@ -31,14 +31,13 @@ import kotlin.jvm.internal.ClassBasedDeclarationContainer
import kotlin.reflect.KotlinReflectionInternalError
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
// Note: this is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
private val moduleData_ = ReflectProperties.lazySoft {
jClass.getOrCreateModule()
protected abstract inner class Data {
// This is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
val moduleData: RuntimeModuleData by ReflectProperties.lazySoft {
jClass.getOrCreateModule()
}
}
val moduleData: RuntimeModuleData
get() = moduleData_()
protected open val methodOwner: Class<*>
get() = jClass
@@ -16,10 +16,7 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.load.java.structure.reflect.classId
@@ -31,27 +28,33 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import kotlin.reflect.KCallable
internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl() {
private val descriptor = ReflectProperties.lazySoft {
with(moduleData) {
packageFacadeProvider.registerModule(moduleName)
module.getPackage(jClass.classId.packageFqName)
private inner class Data : KDeclarationContainerImpl.Data() {
val descriptor: PackageViewDescriptor by ReflectProperties.lazySoft {
with(moduleData) {
packageFacadeProvider.registerModule(moduleName)
module.getPackage(jClass.classId.packageFqName)
}
}
val methodOwner: Class<*> by ReflectProperties.lazy {
val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName
// We need to check isNotEmpty because this is the value read from the annotation which cannot be null.
// The default value for 'xs' is empty string, as declared in kotlin.Metadata
// TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor
if (facadeName != null && facadeName.isNotEmpty()) {
jClass.classLoader.loadClass(facadeName.replace('/', '.'))
}
else {
jClass
}
}
}
override val methodOwner: Class<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName
// We need to check isNotEmpty because this is the value read from the annotation which cannot be null.
// The default value for 'xs' is empty string, as declared in kotlin.Metadata
// TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor
if (facadeName != null && facadeName.isNotEmpty()) {
jClass.classLoader.loadClass(facadeName.replace('/', '.'))
}
else {
jClass
}
}
private val data = ReflectProperties.lazy { Data() }
internal val scope: MemberScope get() = descriptor().memberScope
override val methodOwner: Class<*> get() = data().methodOwner
private val scope: MemberScope get() = data().descriptor.memberScope
override val members: Collection<KCallable<*>>
get() = getMembers(scope, declaredOnly = false, nonExtensions = true, extensions = true).filter { member ->