被用坏的DexClassLoader,关于DexClassLoader内存泄露的问题

本文通过一个具体的Android应用案例探讨了内存泄漏的问题,主要关注类加载器(ClassLoader)导致的内存泄漏现象。文章提供了一段测试代码,展示了如何通过加载外部APK文件中的类来模拟内存泄漏,并解释了即使调用垃圾回收也无法回收类加载器的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原因:

目前有两种猜测,第一种就是类加载容易,但是类卸载就不那么容易了,第二种猜测就是类缓存机制,从classloader的运行过程可以分析到有个过程是缓存常用类的,感觉这个过程没法释放,因为他会认为你以后会一直用。

测试内存泄露代码:

可以用DDMS测试下,当你调用GC的时候byte是可以回收的,但是classloader就没法回收了,也许你认为是因为调用了activity的classloader造成的,但是回头想想这个classloader可是整个应用的,除非你关闭应用,否则无法去掉classloader的引用。
package com.example.test04;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.channels.FileChannel;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import dalvik.system.DexClassLoader;

public class MainActivity extends Activity implements OnClickListener {
	Button test;
	Button test1;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		test = (Button) findViewById(R.id.test);
		test.setOnClickListener(this);

		test1 = (Button) findViewById(R.id.test1);
		test1.setOnClickListener(this);

	}

	@Override
	public void onClick(View v) {
		if (v.equals(test)) {
			LoadClass();
		} else if (v.equals(test1)) {
			LoadData();
		}
	}

	private void LoadData() {
		if (progressDialog == null) {
			progressDialog = new ProgressDialog(this);
		}
		progressDialog.show();
		byte[] buffer = new byte[128 * 1024];
		progressDialog.hide();
	}

	ProgressDialog progressDialog;

	@SuppressLint("NewApi")
	private void LoadClass() {
		if (progressDialog == null) {
			progressDialog = new ProgressDialog(this);
		}
		progressDialog.show();
		try {
			String path = getFilesDir().getAbsolutePath() + File.separator;
			File source = new File(Environment.getExternalStorageDirectory() + "/Test03.apk");
			File target = new File(path + "Test03.apk");
			String targetpath = target.getAbsolutePath();
			if (!target.exists()) {
				copyFile(source, target);
			}
			WeakReference<ClassLoader> reference = new WeakReference<ClassLoader>(getClassLoader());
			DexClassLoader classLoader = new DexClassLoader(targetpath, path, null, reference.get());
			// 加载到了类
			Class<?> mClass = classLoader.loadClass("com.test.Root");
		} catch (Exception e) {

		}
		progressDialog.hide();
	}

	/**
	 * 
	 * 复制文件
	 * 
	 * @param source
	 *            - 源文件
	 * 
	 * @param target
	 *            - 目标文件
	 * 
	 */
	public static void copyFile(File source, File target) {

		FileInputStream fi = null;
		FileOutputStream fo = null;

		FileChannel in = null;

		FileChannel out = null;

		try {
			fi = new FileInputStream(source);

			fo = new FileOutputStream(target);

			in = fi.getChannel();// 得到对应的文件通道

			out = fo.getChannel();// 得到对应的文件通道

			in.transferTo(0, in.size(), out);// 连接两个通道,并且从in通道读取,然后写入out通道

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fi.close();

				in.close();

				fo.close();

				out.close();

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}


最新的解决方案已经出来了,觉得流氓的就不要用了,那就是设置子进程,然后用子进程启动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值