java 类加载器卸载_java - Android卸载类加载器导致崩溃 - 堆栈内存溢出

在开发一个Android应用并尝试实现插件功能时,遇到一个问题:当卸载加载了额外dex文件的类加载器后,应用会在几秒钟或几分钟后崩溃。异常信息显示为`AssertionError: Failed to close dex file in finalizer`,并且最终导致本机堆栈崩溃。问题可能与类加载器未正确清理或dex文件在卸载后仍被引用有关。
摘要由CSDN通过智能技术生成

我正在开发一个Android应用,并且正在尝试类似插件的功能,该功能将允许加载其他dex文件以扩展应用功能。 我已经弄清楚了如何加载扩展PathClassLoader的其他dex文件,而只需进行一些小的更改即可允许其他模块进行通信。 问题是,当dex文件首次在应用程序运行时被加载到应用程序中时,它们都能正常运行,然后,如果我决定禁用此模块,以便卸载类加载器,则该应用程序将继续正常工作几秒钟,然后它将引发异常(仍然继续正常工作),然后在几秒钟/分钟后再次出现(有时甚至需要5分钟),应用程序因本机堆栈跟踪而崩溃。 如果我决定再次加载之前禁用的模块,则只会增加崩溃的机会。

这是卸载模块类加载器几秒钟后发生的情况:

12-27 01:57:10.839 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.840 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

12-27 01:57:10.840 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.840 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

12-27 01:57:10.841 E/System: Uncaught exception thrown by finalizer

12-27 01:57:10.841 E/System: java.lang.AssertionError: Failed to close dex file in finalizer.

at dalvik.system.DexFile.finalize(DexFile.java:336)

at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)

at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)

at java.lang.Daemons$Daemon.run(Daemons.java:103)

at java.lang.Thread.run(Thread.java:764)

然后几秒钟或几分钟后,发生本机崩溃:

12-27 01:57:15.409 A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

12-27 01:57:15.409 A/DEBUG: Build fingerprint: 'Sony/G8341/G8341:8.0.0/47.1.A.8.49/3744219090:user/release-keys'

12-27 01:57:15.409 A/DEBUG: Revision: '0'

12-27 01:57:15.409 A/DEBUG: ABI: 'arm64'

12-27 01:57:15.409 A/DEBUG: pid: 17551, tid: 17697, name: Profile Saver >>> com.rowl.plugdj

12-27 01:57:15.409 A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x657a6983

12-27 01:57:15.409 A/DEBUG: x0 00000000657a6973 x1 0000007bb0739460 x2 0000007bb0600000 x3 0000000000000002

12-27 01:57:15.409 A/DEBUG: x4 0000000000000139 x5 0000007bb073947f x6 2f6d65747379732f x7 726f77656d617266

12-27 01:57:15.409 A/DEBUG: x8 0000000013954588 x9 aec629c3012f5dc2 x10 0000000000000139 x11 000000000000004c

12-27 01:57:15.409 A/DEBUG: x12 656d6172662f6b72 x13 72616a2e6b726f77 x14 000000000000000d x15 aaaaaaaaaaaaaaab

12-27 01:57:15.409 A/DEBUG: x16 0000007bca457cc8 x17 0000007bca3f5f60 x18 0000007bc9c07eb0 x19 0000007baecba270

12-27 01:57:15.409 A/DEBUG: x20 0000007ba6ab68c0 x21 0000007baecba588 x22 0000007ba6ab4298 x23 0000007baa692b18

12-27 01:57:15.409 A/DEBUG: x24 0000000000000000 x25 0000000000002710 x26 00000000977434b0 x27 0000000097746934

12-27 01:57:15.409 A/DEBUG: x28 0000007bc99b6b70 x29 0000007baecba1d0 x30 0000007bc96cf67c

12-27 01:57:15.409 A/DEBUG: sp 0000007baecba0c0 pc 0000007bc96cf634 pstate 0000000080000000

12-27 01:57:15.411 A/DEBUG: backtrace:

12-27 01:57:15.411 A/DEBUG: #00 pc 00000000002fe634 /system/lib64/libart.so (_ZN3art3jit12JitCodeCache18GetProfiledMethodsERKNSt3__13setINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS2_4lessIS9_EENS7_IS9_EEEERNS2_6vectorINS_17ProfileMethodInfoENS7_ISH_EEEE+228)

12-27 01:57:15.411 A/DEBUG: #01 pc 000000000030ac08 /system/lib64/libart.so (_ZN3art12ProfileSaver20ProcessProfilingInfoEbPt+1452)

12-27 01:57:15.411 A/DEBUG: #02 pc 00000000003099ac /system/lib64/libart.so (_ZN3art12ProfileSaver3RunEv+704)

12-27 01:57:15.411 A/DEBUG: #03 pc 000000000030b7b8 /system/lib64/libart.so (_ZN3art12ProfileSaver21RunProfileSaverThreadEPv+88)

12-27 01:57:15.411 A/DEBUG: #04 pc 00000000000667d0 /system/lib64/libc.so (_ZL15__pthread_startPv+36)

12-27 01:57:15.411 A/DEBUG: #05 pc 000000000001f2a4 /system/lib64/libc.so (__start_thread+68)

这是我正在使用的类加载器

public class ExtensionClassLoader extends PathClassLoader {

private final Map> classes = new HashMap();

private final ExtensionManager extensionManager;

public ExtensionClassLoader(ExtensionManager extensionManager, String dexPath, String librarySearchPath, ClassLoader parent) {

super(dexPath, librarySearchPath, parent);

this.extensionManager = extensionManager;

}

@Override

protected Class> findClass(String name) throws ClassNotFoundException {

if (name.startsWith("com.rowl.") || name.startsWith("dj.plug.")) {

throw new ClassNotFoundException(name);

}

PlugDJ.d("Find Class: " + name);

Class clazz = classes.get(name);

if(clazz == null){

clazz = extensionManager.getClass(name);

if(clazz == null){

clazz = super.findClass(name);

if(clazz != null){

extensionManager.addClass(name, clazz);

}

}

if(clazz != null){

classes.put(name, clazz);

}

}

return clazz;

}

@Override

public String findLibrary(String name) {

return super.findLibrary(name);

}

public Set getClasses(){

return classes.keySet();

}

@Override

protected void finalize() throws Throwable {

super.finalize();

}

}

另外,这里是加载这些模块的类:

class ExtensionManager(plugDJInstance: PlugDJ) : Manager(plugDJInstance) {

private val classes = HashMap>()

private val extensionLoaders = HashMap()

private val loadedExtensions = ArrayList()

private val availableExtensions = ArrayList()

val plugAPI = PlugAPIImpl(pdjInstance as PlugDJAndroid)

private val context by lazy { (pdjInstance as PlugDJAndroid).context }

private val extensionSettings by lazy { context.getSharedPreferences("ext", Context.MODE_PRIVATE)}

private val whitelistedExtensions by lazy { extensionSettings.getStringSet("whitelistedExtensions", HashSet()) }

fun addClass(name: String, clazz: Class){

if(!classes.containsKey(name)){

classes.put(name, clazz)

}

}

fun removeClass(name: String){

classes.remove(name)

}

fun getClass(name: String): Class?{

return classes[name]

}

private fun loadApk(dir: File, dexOutputDir: File, parent: ClassLoader): ExtensionClassLoader{

val files = StringBuilder()

dir.listFiles().filter { it.extension == "apk" }.forEach {

files.append(it.absolutePath).append(":")

}

files.deleteCharAt(files.length - 1)

return ExtensionClassLoader(this, files.toString(), dexOutputDir.absolutePath.toString(), parent)

}

fun isExtensionWhitelisted(extInfo: PDJExtensionInfo): Boolean{

return whitelistedExtensions.contains(extInfo.appPackage)

}

@SuppressLint("ApplySharedPref")

fun addExtensionToWhitelist(extInfo: PDJExtensionInfo){

whitelistedExtensions.add(extInfo.appPackage)

extensionSettings.edit().putStringSet("whitelistedExtensions", whitelistedExtensions).commit()

d("Added " + extInfo.appPackage + " to whitelist")

}

@SuppressLint("ApplySharedPref")

fun removeExtensionFromWhitelist(extInfo: PDJExtensionInfo){

whitelistedExtensions.remove(extInfo.appPackage)

extensionSettings.edit().putStringSet("whitelistedExtensions", if(whitelistedExtensions.isEmpty()) null else whitelistedExtensions).commit()

d("Checking... " + whitelistedExtensions)

d("Removed " + extInfo.appPackage + " from whitelist")

}

fun discoverExtensions() {

try{

whitelistedExtensions.forEach {

d("Whitelisted extension: $it")

}

val installedApps = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

for(app in installedApps){

if(app.sourceDir.contains("system")) continue

if(app.metaData != null && app.metaData.containsKey("pdjExtensionName")){

val extName = app.metaData.getString("pdjExtensionName")

val extMainClass = app.metaData.getString("pdjExtensionMainClass")

val sourceDir = File(app.sourceDir).parentFile

PlugDJ.i("Found extension: " + extName)

var extInfo = getAvailableExtensions().find { it.appPackage == app.packageName }

d(extInfo?.sourceDirectory.toString() )

d(sourceDir.toString() )

if(extInfo != null){

PlugDJ.d("Removing " + app.packageName)

availableExtensions.remove(extInfo)

}

extInfo = PDJExtensionInfoImpl(extName, sourceDir, app.packageName, extMainClass, "", "", null, null)

availableExtensions.add(extInfo)

}

}

}catch (e: Exception){

e.printStackTrace()

}

}

fun getAvailableExtensions(): List{

return Collections.unmodifiableList(ArrayList(availableExtensions))

}

fun getLoadedExtensions(): List{

return Collections.unmodifiableList(ArrayList(loadedExtensions))

}

fun loadExtension(extInfo: PDJExtensionInfo): PDJExtension{

var cl = context.classLoader

val dexOutputDir = context.getDir("dex", Context.MODE_PRIVATE)

val extcl = loadApk(extInfo.sourceDirectory, dexOutputDir, cl)

extensionLoaders[extInfo.name] = extcl

val mainClass = extcl.loadClass(extInfo.mainClass)

val apiField = mainClass.superclass.getDeclaredField("api")

val clField = mainClass.superclass.getDeclaredField("classLoader")

val extField = mainClass.superclass.getDeclaredField("extensionInfo")

val extension = mainClass.newInstance() as PDJExtension

apiField.apply {

isAccessible = true

set(extension, plugAPI)

isAccessible = false

}

clField.apply {

isAccessible = true

set(extension, mainClass.classLoader)

isAccessible = false

}

extField.apply {

isAccessible = true

set(extension, extInfo)

isAccessible = false

}

loadedExtensions.add(extension)

return extension

}

fun getLoadedExtension(appPackage: String): PDJExtension? {

return getLoadedExtensions().find { it.extensionInfo.appPackage == appPackage }

}

fun enableExtension(ext: PDJExtension){

ext.onEnable()

ext::class.java.superclass.getDeclaredField("isEnabled").apply {

isAccessible = true

set(ext, true)

isAccessible = false

}

}

fun disableExtension(ext: PDJExtension){

try{

plugAPI.removeEventListeners(ext)

ext.onDisable()

}catch (e: Exception){

e.printStackTrace()

}

ext::class.java.superclass.getDeclaredField("isEnabled").apply {

isAccessible = true

set(ext, false)

isAccessible = false

}

}

fun unloadExtension(ext: PDJExtension){

d("Unloading extension...")

ext::class.java.superclass.getDeclaredField("api").apply {

isAccessible = true

set(ext, null)

isAccessible = false

}

val cl = ext.classLoader as ExtensionClassLoader

loadedExtensions.remove(ext)

for(className in cl.classes){

removeClass(className)

}

extensionLoaders.remove(ext.extensionInfo.name)

d("Extension unloaded.")

System.runFinalization()

System.gc()

}

}

知道是什么原因造成的吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值