故事开始:忙碌的Android王国
在Android王国里,有一个叫做"内存村"的地方,这里住着各种各样的类(Class)居民。每个类都有自己的房子(内存空间),房子里住着它们的家人(方法、变量等)。
有一天,内存村出现了住房危机------内存不够用了!村长GC(垃圾回收)决定要清理掉那些"没人用"的类房子。
主角登场:ClassLoader家族
在Android王国,有三个主要的ClassLoader家族:
java
// ClassLoader家族族谱
public class ClassLoaderFamily {
// 老祖宗:启动类加载器(C++实现,Java中看不到)
// 大管家:系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 二管家:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
// 小管家:应用类加载器(我们APP的)
ClassLoader appClassLoader = getClass().getClassLoader();
// 还有各种自定义的ClassLoader
class MyCustomClassLoader extends DexClassLoader {
// 可以加载dex、apk中的类
}
}
什么情况下类会被回收?
场景1:普通的类居民
java
public class NormalClass {
private static String staticData = "我是静态数据";
public void doSomething() {
System.out.println("我在工作");
}
// 当满足以下条件时,这个类可以被回收:
// 1. 没有实例对象存在
// 2. 加载它的ClassLoader被回收
// 3. 没有其他地方引用这个Class对象
// 4. 没有在其他地方被主动引用(如反射)
}
场景2:特殊的静态居民
java
public class ClassWithStaticReference {
// 这个静态引用会阻止类被回收!
private static Object staticHolder = new Object();
// 这个也会!
private static final String IMPORTANT_DATA = "重要数据";
// 但如果这样写,就不会阻止回收
private static class WeakStaticHolder {
private static WeakReference<Object> weakRef =
new WeakReference<>(new Object());
}
}
类的生命周期:从出生到退休
java
public class ClassLifecycle {
// 1. 加载阶段 - 搬进内存村
static {
System.out.println("我正在被加载!");
}
// 2. 连接阶段 - 办理入住手续
// - 验证:检查身份证是否合法
// - 准备:分配静态变量内存
// - 解析:把符号引用转成直接引用
// 3. 初始化阶段 - 安家落户
// - 执行<clinit>方法(静态代码块)
// 4. 使用阶段 - 正常工作
// 5. 卸载阶段 - 搬出内存村(满足条件时)
}
实战演示:如何观察类的回收
java
public class ClassGCWatcher {
public static void main(String[] args) throws Exception {
watchClassGC();
}
public static void watchClassGC() throws Exception {
// 创建自定义ClassLoader来加载测试类
ClassLoader customLoader = new CustomClassLoader();
// 加载我们的测试类
Class<?> targetClass = customLoader.loadClass("com.example.TempClass");
// 创建弱引用来监视Class对象
WeakReference<Class<?>> classRef = new WeakReference<>(targetClass);
// 清除所有强引用
targetClass = null;
customLoader = null;
// 触发GC
System.gc();
System.runFinalization();
// 检查类是否被回收
if (classRef.get() == null) {
System.out.println("🎉 类被成功回收了!");
} else {
System.out.println("❌ 类还在内存中");
}
}
static class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 模拟从dex/apk中加载类
byte[] classData = loadClassData(name);
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String className) {
// 这里应该是从文件读取类的字节码
// 为了演示,我们返回空数组
return new byte[0];
}
}
}
时序图:类的完整生命周期

阻止类回收的"钉子户"
java
public class ClassGCPreventer {
// 情况1:静态变量持有引用
public static List<Object> staticList = new ArrayList<>();
// 情况2:单例模式
private static ClassGCPreventer instance = new ClassGCPreventer();
public static ClassGCPreventer getInstance() { return instance; }
// 情况3:线程持有引用
private Thread livingThread = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
// 线程运行时,其上下文ClassLoader会阻止类回收
} catch (InterruptedException e) {
break;
}
}
});
// 情况4:JNI全局引用
// native方法中创建的全局引用也会阻止回收
}
优化技巧:做个"好公民"类
java
public class GoodCitizenClass {
// 技巧1:使用弱引用避免内存泄漏
private WeakReference<Context> weakContext;
// 技巧2:及时清理静态引用
public static void clearStaticResources() {
staticCache.clear();
staticHolder = null;
}
// 技巧3:避免在静态块中创建重对象
private static Map<String, String> lightWeightCache =
new ConcurrentHashMap<>(); // 轻量级缓存
// 技巧4:使用适当的ClassLoader
public void loadClassWisely() {
// 对于临时类,使用独立的ClassLoader
ClassLoader tempLoader = new TemporaryClassLoader();
// 用完及时置空
tempLoader = null;
}
static class TemporaryClassLoader extends ClassLoader {
@Override
protected void finalize() throws Throwable {
// 清理资源
super.finalize();
}
}
}
Android特殊场景
java
public class AndroidClassGC {
// 场景1:插件化框架中的类卸载
public void hotPatch() {
// 使用独立的DexClassLoader加载补丁
DexClassLoader patchLoader = new DexClassLoader(
patchPath, optimizedDirectory, null, parentLoader);
// 当需要卸载补丁时
patchLoader = null;
System.gc();
}
// 场景2:动态特性交付
public void dynamicFeature() {
// 使用SplitCompat
// 动态特性有自己的ClassLoader
// 当特性不再需要时可以整体卸载
}
// 场景3:WebView中的类管理
public void webViewScenario() {
WebView webView = new WebView(context);
// WebView会创建自己的ClassLoader
// 及时销毁WebView有助于类回收
webView.destroy();
}
}
总结:类的回收法则
-
必要条件:
- 类的所有实例都已被回收
- 加载该类的ClassLoader已被回收
- 该类对应的java.lang.Class对象没有被任何地方引用
-
常见阻碍:
- 静态变量持有对象引用
- 单例模式长期持有
- 线程未结束
- JNI全局引用
- 反射缓存
-
优化建议:
- 使用弱引用存储上下文
- 及时清理静态资源
- 为临时功能使用独立ClassLoader
- 避免在静态块中创建重对象
记住,在Android王国里,做一个"懂得及时离开"的好类公民,才能让内存村保持整洁高效!🏡✨