- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我在我的 Android 应用程序中使用了一些 native 库,我想在某个时间点从内存中卸载它们。当加载 native 库的加载类的 ClassLoader 被垃圾收集时,库将被卸载。灵感:native unloading .
在下面的代码中,我通过省略本地库加载代码来简化案例,只是演示了 Classloader 内存泄漏。
我正在 Android KitKat 4.4.2,API 19 上对此进行测试。设备:摩托罗拉 Moto G。
为了演示,我有以下 ClassLoader,从 PathClassLoader
派生,用于加载 Android 应用程序。
package com.demo;
import android.util.Log;
import dalvik.system.PathClassLoader;
public class LibClassLoader extends PathClassLoader {
private static final String THIS_FILE="LibClassLoader";
public LibClassLoader(String dexPath, String libraryPath, ClassLoader parent) {
super(dexPath, libraryPath, parent);
}
@Override
protected void finalize() throws Throwable {
Log.v(THIS_FILE, "Finalizing classloader " + this);
super.finalize();
}
}
我有 EmptyClass
与 LibClassLoader
一起加载。
package com.demo;
public class EmptyClass {
}
而内存泄漏是由以下代码引起的:
final Context ctxt = this.getApplicationContext();
PackageInfo pinfo = ctxt.getPackageManager().getPackageInfo(ctxt.getPackageName(), 0);
LibClassLoader cl2 = new LibClassLoader(
pinfo.applicationInfo.publicSourceDir,
pinfo.applicationInfo.nativeLibraryDir,
ClassLoader.getSystemClassLoader()); // Important: parent cannot load EmptyClass.
if (memoryLeak){
Class<?> eCls = cl2.loadClass(EmptyClass.class.getName());
Log.v("Demo", "EmptyClass loaded: " + eCls);
eCls=null;
}
cl2=null;
// Try to invoke GC
System.runFinalization();
System.gc();
Thread.sleep(250);
System.runFinalization();
System.gc();
Thread.sleep(500);
System.runFinalization();
System.gc();
Debug.dumpHprofData("/mnt/sdcard/hprof"); // Dump heap, hardcoded path...
需要注意的重要一点是 cl2
的父级不是 ctxt.getClassLoader()
,即加载演示代码类的类加载器。这是设计使然,因为我们不希望 cl2
使用它的父级来加载 EmptyClass
。
问题是,如果 memoryLeak==false
,那么 cl2
就会被垃圾回收。如果memoryLeak==true
,就会出现内存泄漏。这种行为与标准 JVM 上的观察结果不一致(我使用 [1] 中的类加载器来模拟相同的行为)。在 JVM 上,两种情况下 cl2
都会被垃圾回收。
我还用 Eclipse MAT 分析了堆转储文件,cl2
没有被垃圾收集,因为类 EmptyClass
仍然持有对它的引用(因为类在其类加载器上持有引用) .这是有道理的。但是 EmptyClass
显然不是无缘无故地收集垃圾。 GC 根的路径就是这个 EmptyClass
。我没能说服 GC 完成 EmptyClass
。
memoryLeak==true
的 HeapDump 文件位于 here , Eclipse Android 项目,带有此内存泄漏的演示应用程序here .
我还尝试了在 LibClassLoader
中加载 EmptyClass
的另一种变体,即 Class.forName(...)
或 cl2.findClass()
。有/没有静态初始化,结果总是一样的。
我查了很多网上资源,据我所知没有涉及到静态缓存字段。我检查了 PathClassLoader
的源代码,它是父类,我没有发现任何问题。
非常感谢您的见解和任何帮助。
System.gc()
也只是为 JVM/Dalvik 执行 GC 的提示。我只是想知道为什么会出现内存泄漏。为了让 Erik Hellman 写的更清楚,我说的是加载 NDK 编译的 C/C++ 库,动态链接,带有 .so 后缀。
最佳答案
首先,让我们整理一下这里的术语。
它是您想要加载的带有 JNI 绑定(bind)的 native 库吗?也就是使用 Android NDK 在 C/C++ 中实现的后缀为 .so 的文件?这通常是我们在谈论原生库时所指的。如果是这种情况,那么解决此问题的唯一方法是在单独的进程中运行库。最简单的方法是创建一个 Android 服务,在其中为 list 中的条目添加 android:process=":myNativeLibProcess"
。然后,该服务将像往常一样调用 System.loadLibrary()
,然后您使用 Context.bindService()
从您的主进程绑定(bind)到该服务。
如果它是 JAR 文件中的一组 Java 类,那么我们正在寻找其他东西。对于 Android,您需要创建将库代码编译成 DEX 文件,该文件被放入 JAR 文件并使用 DexClassLoader
加载,类似于您在代码中所做的。当您想卸载库时,您需要释放对您创建的实例的所有引用以及用于加载库的类加载器。这将允许您稍后加载库的新版本。唯一的问题是,您不会在 API 级别 19 或更低级别的设备(即使用 Dalvik VM 的 Android 版本)上回收卸载库使用的所有内存,因为类定义不会被垃圾收集。对于 Lollipop 及更高版本,新 VM 还将垃圾收集类定义,因此对于这些设备,这将更好地工作。
希望对您有所帮助。
关于Android ClassLoader 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24225572/
我只是想知道如果我知道类加载器,我是否可以在 JVM 运行时获取类的实例(对对象的引用)。请引用下面的代码来理解我的问题。 A类: package local.run; public class A
我们正在尝试调试 WebStart 的一个不可重现的问题,即访问 Jars 内的资源将“随机”失败。也许每 1000 个应用程序运行中就有一个会出现此错误,这种错误可能发生在从 jar 读取资源的任何
在从属实例(AIX 5.3 上的主服务器;Windows 2008(R2) 上的从属服务器)上运行 Maven2 作业时,我收到以下消息(下面粘贴了完整的堆栈跟踪):“java.lang.ClassN
我正在编写一个游戏引擎,我需要在其中分析程序中提到的每个类。由于这是一个游戏引擎,因此它将作为 JAR 文件附加到客户的项目中。从该 JAR 文件中,我需要能够扫描客户端正在使用的每个类。 所以我想我
Java安全之BCEL ClassLoader 写在前面 BCEL平常在测试反序列化的时候也经常会用到,比如延时测Gadget以及在某些场景下执行命令不是那么顺手的情况下选择BCEL去打内存马,就像F
比如说,我有一个 A 类,由 ClassLoader CL1 加载。 我有另一个 B 类,由 ClassLoader CL2 加载。 假设这两个类现在都由各自的类加载器加载。 如果我从 A 执行以下语
在 org.dozer.BeanFactory.createBean(Object, Class, String) 的实现中我尝试将对象转换为它的类型。如果我部署所有 bundle ,即关闭并启动所有
它过去曾经可以工作,但是我中间没有发生什么,现在它总是返回null。 要读取的文件在项目root diretory中,该项目与Paths.get(".")的输出相对应。 注意:功能是顶级的 我正在读取
在 org.dozer.BeanFactory.createBean(Object, Class, String) 的实现中我尝试将对象转换为它的类型。如果我部署我所有的包,即关闭并启动所有包,我得到
我必须使用第 3 方平台,但该平台具有无法替换的较旧版本的 jar libjar-1.0.0.jar。该平台允许我在其上运行我自己的(平面文件)包。我将新版本的 libjar-2.0.0.jar 放在
我正在尝试使用下面的类加载器在我的 Maven 插件中加载特定的类: public ClassLoader getClassLoader(MavenProject project) { tr
以下场景: CustomClassLoaderA 加载 ClassA CustomClassLoaderB 加载 ClassB 到目前为止一切顺利,但是: CustomClassLoaderA 应该能
我试图强制使用我的自定义类加载器加载某些类,问题是在加载调用类之后仍然不知道类定义并尝试再次加载它,当然之后我们对该类有两种不同的定义,并将一种定义分配给另一种会导致类转换异常。有什么建议或想法可以解
我在尝试指示 Java 类加载器在部署后从 test/resources 目录中检索 JSON 文件时遇到了问题。 public class TestFileUtil { private st
我似乎在为我正在开发的应用程序的模块加载器中加载类时遇到问题。基本上,我将用它加载的所有类都扩展了另一个类,该类位于实际应用程序的包中。出于我们的目的,我们将其称为模块。模块位于实际应用程序之外的单独
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 8 年前。 Improve t
我正在使用 URLClassLoader 创建一个新的类加载器,并尝试将其设置为当前线程的类加载器。但它对我来说不能正常工作。按照我的理解,如果我给当前线程设置一个classLoader,那么当前线程
昨天我想到了一个问题,下面是细节: 我有 3 个 JAR 文件,a.jar、b.jar、c.jar。这两个 jars 文件都有一个名为 com.test.Test 的类,并且 sayHello() 是
我想创建一个新的 ClassLoader 实例,它在开始时绝对不包含任何类。它不应该有一个可用的类。甚至没有 java.lang 类。我希望能够为我自己的位置手动加载每个类。 (使用覆盖的 defin
我正在开发一个个人用药时间提醒应用程序。除了在通知几次用药时间后停止之外,它几乎可以正常工作。在监视应用程序的日志猫时,我发现它在收到以下警告后正在停止警报接收器。 W/System: ClassLo
我是一名优秀的程序员,十分优秀!