前言
最近在查看线上上报上来的卡顿数据,发生卡顿时长较久的的卡顿都是发生在内存不足的时候,基本上系统可用内存都只有几百k或者1、2M,于是就怀疑我们应用存在内存泄漏。拿了手上刷了Android 12的Pixel 3开始排查
奇怪的引用链
按照我们应用之前的尿性,内存泄漏经常是在接力房这个页面。跑了一遍进房 → 上麦 → 回到首页的场景,demp出内存情况,果然有泄漏
看了这5个泄漏点,其实都是跟RoomLiveGameActivity相关,接着看下是谁还在持有RoomLiveGameActivity
纳尼,ConnectivityManager竟然持有了RoomLiveGameActivity而导致内存泄漏。没办法,又要开始苦逼的看源码了。
分析
一般我们在获取ConnectivityManager都是这样获取的
Activity.java
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
而Activity是继承于ContextWrapper,最终调用到ContextWrapper的getSystemService方法
ContextWrapper.java
@Override
public Object getSystemService(String name) {
// 这里的mBase是ContextImpl,后面会讲到
return mBase.getSystemService(name);
}
ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
所有需要用到的系统服务,都是从SystemServiceRegistry获取的,然后通过各个Manager调用binder接口与系统通信,我们看看SystemServiceRegistry的实现:
SystemServiceRegistry.java
public static Object getSystemService(ContextImpl ctx, String name) {
// ServiceFetcher是一个接口,通过服务名匹配相应的Fetcher,获取对应的Manager
final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
...
final Object ret = fetcher.getService(ctx);
...
return ret;
}
static {
// 在static块中,初始化了CONNECTIVITY_SERVICE对应的Fetcher
ConnectivityFrameworkInitializer.registerServiceWrappers();
...
}
ConnectivityFrameworkInitializer.java
public static void registerServiceWrappers() {
// 这里又调回SystemServiceRegistry的静态方法,注册CONNECTIVITY_SERVICE对应的服务
// 最后一个参数是一个lambda表达式,它是一个ContextAwareServiceProducerWithBinder接口
SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class,
(context, serviceBinder) -> {
IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
return new ConnectivityManager(context, icm);
}
);
}
public interface ContextAwareServiceProducerWithBinder<TServiceClass> {
@NonNull
TServiceClass createService(@NonNull Context context, @NonNull IBinder serviceBinder);
}
SystemServiceRegistry.java
public static <TServiceClass> void registerContextAwareService(
@NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
@NonNull ContextAwareServiceProducerWithBinder<TServiceClass> serviceProducer) {
// 可以看到CONNECTIVITY_SERVICE对应的是CachedServiceFetcher,而CachedServiceFetcher的createService又调用了ContextAwareServiceProducerWithBinder的createService方法(就是上面的lambda表达式)
// context是从ContextImpl获取出来的OuterContext,这个很重要(敲黑板),后面会讲到
registerService(serviceName, serviceWrapperClass,
new CachedServiceFetcher<TServiceClass>() {
@Override
public TServiceClass createService(ContextImpl ctx)
throws ServiceNotFoundException {
// 这里传一个ContextImpl的OuterContext给ConnectivityManager做初始化用
return serviceProducer.createService(
ctx.getOuterContext(),
ServiceManager.getServiceOrThrow(serviceName));
}});
}
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
@Override
public final T getService(ContextImpl ctx) {
// 这里先检查有没有相应的缓存,有的话直接返回
final Object[] cache = ctx.mServiceCache;
T service = (T) cache[mCacheIndex];
if (service != null) {
return service;
}
// 没有找到缓存的话,通过createService创建
...
T service = null;
@ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
try {
service = createService(ctx);
newState = ContextImpl.STATE_READY;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
return service;
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
ConnectivityManager.java
public class ConnectivityManager {
private static ConnectivityManager sInstance;
public ConnectivityManager(Context context, IConnectivityManager service) {
mContext = Objects.requireNonNull(context, "missing context");
mService = Objects.requireNonNull(service, "missing IConnectivityManager");
// 初始化的时候,把自己赋值给静态变量sInstance
sInstance = this;
}
}
总结一下获取ConnectivityManager的调用流程:
- 通过getSystemService获取ConnectivityManager
- 最终会调用到ContextImpl的getSystemService方法
- 接着通过SystemServiceRegistry的getSystemService方法
- SystemServiceRegistry内部通过CachedServiceFetcher获取
- CachedServiceFetcher又通过ContextAwareServiceProducerWithBinder创建ConnectivityManager返回给调用者
前面讲到,ContextImpl中的OuterContext很重要,要知道这个OuterContext是什么,得看看Activity的启动流程:
ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// 这里创建一个ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
if (activity != null) {
// 这里可以看到OuterContext其实就是Activity
appContext.setOuterContext(activity);
// 上面讲到ContextWrapper中的mBase就是在attach方法被赋值的
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
}
...
}
至此,内存泄漏的原因找到了。在ConnectivityManager的构造方法中可以看到,有个静态变量持有自己,并且持有了传进来的context,这个context是ContextImpl的OuterContext。
如果调用getSystemService的context是Activity的话,那么ContextImpl的OuterContext就是Activity。
如果调用getSystemService的context是Application的话,那么ContextImpl的OuterContext就是Application。
所以解决这个内存泄漏的方法也很简单,就是用Application的context去获取ConnectivityManager就能保证不发生内存泄漏,代码这里就不贴了。
结束了吗?
上面讲的全是Android 12的源码,但是在别的版本又是不一样的情况,先说说Android 7到Andoid 11的版本:
SystemServiceRegistry.java
static {
// 在static块中,去初始化CONNECTIVITY_SERVICE对应的Fetcher
registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
@Override
public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
return new ConnectivityManager(context, service);
}});
}
static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
private T mCachedInstance;
@Override
public final T getService(ContextImpl ctx) {
synchronized (StaticApplicationContextServiceFetcher.this) {
if (mCachedInstance == null) {
// 可以看到,已经帮我们去获取ApplicationContext了,所以在Android 7到11的版本是不存在泄漏的情况。
Context appContext = ctx.getApplicationContext();
try {
mCachedInstance = createService(appContext != null ? appContext : ctx);
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return mCachedInstance;
}
}
}
我们再看看Android 6:
SystemServiceRegistry.java
static {
// 在static块中,去初始化CONNECTIVITY_SERVICE对应的Fetcher
registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
@Override
public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
return new ConnectivityManager(context, service);
}});
}
static abstract class StaticOuterContextServiceFetcher<T> implements ServiceFetcher<T> {
private T mCachedInstance;
@Override
public final T getService(ContextImpl ctx) {
synchronized (StaticOuterContextServiceFetcher.this) {
if (mCachedInstance == null) {
mCachedInstance = createService(ctx.getOuterContext());
}
return mCachedInstance;
}
}
}
可以看到调用createService方法时,传的还是ContextImpl的OuterContext,所以在Android 6的版本,是存在泄漏的情况的可能。
总结
在需要调用系统服务的场景,能用applicationContext还是尽量用,避免出现类似的情况。
写完了,下篇见。