Memory optimization
diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt
index 7faccd8..fd10c9e 100644
--- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt
+++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt
@@ -9,6 +9,7 @@
 import com.intellij.openapi.components.ApplicationComponent
 import com.intellij.openapi.vfs.*
 import com.intellij.util.containers.ContainerUtil.createConcurrentSoftValueMap
+import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
 import org.jetbrains.kotlin.konan.library.KLIB_METADATA_FILE_EXTENSION
 import org.jetbrains.kotlin.konan.library.KLIB_MODULE_METADATA_FILE_NAME
 import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
@@ -23,7 +24,7 @@
             ApplicationManager.getApplication().getComponent(KotlinNativeLoadingMetadataCache::class.java)
     }
 
-    private val packageFragmentCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>()
+    private val packageFragmentCache = createConcurrentWeakValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>()
     private val moduleHeaderCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataLibrary>()
 
     fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment =
diff --git a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt
index e86679c..d9b71c8 100644
--- a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt
+++ b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt
@@ -33,3 +33,21 @@
         return ClassData(nameResolver, foundClass, KonanMetadataVersion.INSTANCE, SourceElement.NO_SOURCE)
     }
 }
+
+class KotlinNativeProtoBasedClassDataFinder(
+    proto: KonanProtoBuf.LinkDataPackageFragment,
+    private val nameResolver: NameResolver,
+    private val classSource: (ClassId) -> SourceElement = { SourceElement.NO_SOURCE }
+) : ClassDataFinder {
+    private val classIdToProto =
+        proto.classes.classesList.associateBy { klass ->
+            nameResolver.getClassId(klass.fqName)
+        }
+
+    internal val allClassIds: Collection<ClassId> get() = classIdToProto.keys
+
+    override fun findClassData(classId: ClassId): ClassData? {
+        val classProto = classIdToProto[classId] ?: return null
+        return ClassData(nameResolver, classProto, KonanMetadataVersion.INSTANCE, classSource(classId))
+    }
+}
diff --git a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt
index 6e332f5..eba2c56 100644
--- a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt
+++ b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt
@@ -25,7 +25,7 @@
     private val packageAccessedHandler: PackageAccessedHandler?,
     storageManager: StorageManager,
     module: ModuleDescriptor,
-    partName: String
+    val partName: String
 ) : DeserializedPackageFragment(fqName, storageManager, module) {
 
     lateinit var components: DeserializationComponents
@@ -36,7 +36,11 @@
 
     // The proto field is lazy so that we can load only needed
     // packages from the library.
-    private val protoForNames: KonanProtoBuf.LinkDataPackageFragment by lazy { library.packageMetadata(fqName.asString(), partName) }
+    private val protoForNames: KonanProtoBuf.LinkDataPackageFragment
+        get() =
+            protoForNamesStore ?: library.packageMetadata(fqName.asString(), partName).also { protoForNamesStore = it }
+
+    private var protoForNamesStore: KonanProtoBuf.LinkDataPackageFragment? = null
 
     val proto: KonanProtoBuf.LinkDataPackageFragment
         get() = protoForNames.also { packageAccessedHandler?.markPackageAccessed(fqName) }
@@ -46,35 +50,37 @@
     }
 
     override val classDataFinder by lazy {
-        KonanClassDataFinder(proto, nameResolver)
+        KotlinNativeProtoBasedClassDataFinder(proto, nameResolver)
     }
 
     private val _memberScope by lazy {
+        val classNameList = protoForNames.classes.classNameList
         /* TODO: we fake proto binary versioning for now. */
-        DeserializedPackageMemberScope(
+        val memberScope = DeserializedPackageMemberScope(
             this,
             proto.getPackage(),
             nameResolver,
             KonanMetadataVersion.INSTANCE,
             /* containerSource = */ null,
             components
-        ) { loadClassNames() }
+        ) { loadClassNames(classNameList) }
+        // Clear hard ref to proto, as class fully initialized
+        protoForNamesStore = null
+        memberScope
     }
 
     override fun getMemberScope(): DeserializedPackageMemberScope = _memberScope
 
     private val classifierNames: Set<Name> by lazy {
         val result = mutableSetOf<Name>()
-        result.addAll(loadClassNames())
+        result.addAll(loadClassNames(protoForNames.classes.classNameList))
         protoForNames.getPackage().typeAliasList.mapTo(result) { nameResolver.getName(it.name) }
         result
     }
 
     fun hasTopLevelClassifier(name: Name): Boolean = name in classifierNames
 
-    private fun loadClassNames(): Collection<Name> {
-
-        val classNameList = protoForNames.classes.classNameList
+    private fun loadClassNames(classNameList: List<Int>): Collection<Name> {
 
         val names = classNameList.mapNotNull {
             val classId = nameResolver.getClassId(it)