原因:
目前有两种猜测,第一种就是类加载容易,但是类卸载就不那么容易了,第二种猜测就是类缓存机制,从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();
}
}
}
}
最新的解决方案已经出来了,觉得流氓的就不要用了,那就是设置子进程,然后用子进程启动。