(七十七)getSystemService内存泄露探讨

2023-12-19 07:48

本文主要是介绍(七十七)getSystemService内存泄露探讨,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:看了下https://www.jianshu.com/p/5d96983fc6db 这篇文章,文中有提及Android N之前WiFiManager会长时间持有context不释放,导致内存泄露。Android N以后修改其中的asyncChannel为非static的就好了,感觉没说的很清楚。

 

1.getSystemService流程

1.1 ContextImpl

    @Overridepublic Object getSystemService(String name) {return SystemServiceRegistry.getSystemService(this, name);}

 

1.2 SystemServiceRegistry

    /*** Gets a system service from a given context.*/public static Object getSystemService(ContextImpl ctx, String name) {ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);return fetcher != null ? fetcher.getService(ctx) : null;}

这边其实是获取的先前的缓存,但是有个前提是ctx是一样的才可以,也就是说一个应用的两个activity传递各自的context是不会得到复用的,使用ApplicationContext才可以得到复用。

/*** Manages all of the system services that can be returned by {@link Context#getSystemService}.* Used by {@link ContextImpl}.*/
final class SystemServiceRegistry {private static final String TAG = "SystemServiceRegistry";// Service registry information.// This information is never changed once static initialization has completed.private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =new HashMap<Class<?>, String>();private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =new HashMap<String, ServiceFetcher<?>>();private static int sServiceCacheSize;// Not instantiable.private SystemServiceRegistry() { }static {registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,new CachedServiceFetcher<AccessibilityManager>() {@Overridepublic AccessibilityManager createService(ContextImpl ctx) {return AccessibilityManager.getInstance(ctx);}});registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,new CachedServiceFetcher<CaptioningManager>() {@Overridepublic CaptioningManager createService(ContextImpl ctx) {return new CaptioningManager(ctx);}});
...registerService(Context.WIFI_SERVICE, WifiManager.class,new CachedServiceFetcher<WifiManager>() {@Overridepublic WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);IWifiManager service = IWifiManager.Stub.asInterface(b);return new WifiManager(ctx.getOuterContext(), service,ConnectivityThread.getInstanceLooper());}});
...
    /*** Override this class when the system service constructor needs a* ContextImpl and should be cached and retained by that context.*/static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {private final int mCacheIndex;public CachedServiceFetcher() {mCacheIndex = sServiceCacheSize++;}@Override@SuppressWarnings("unchecked")public final T getService(ContextImpl ctx) {final Object[] cache = ctx.mServiceCache;synchronized (cache) {// Fetch or create the service.Object service = cache[mCacheIndex];if (service == null) {try {service = createService(ctx);cache[mCacheIndex] = service;} catch (ServiceNotFoundException e) {onServiceNotFound(e);}}return (T)service;}}public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;}
    /*** Statically registers a system service with the context.* This method must be called during static initialization only.*/private static <T> void registerService(String serviceName, Class<T> serviceClass,ServiceFetcher<T> serviceFetcher) {SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);}

关键代码都贴出来了

  1. 首先SystemServiceRegistry调用之初有个static的代码块,里面会将很多service的serviceFetcher存进static集合SYSTEM_SERVICE_FETCHERS中。
  2. 之后有调用getSystemService,从static集合SYSTEM_SERVICE_FETCHERS 中取出对应service的serviceFetcher,接着调用getService方法。
  3. getService获取的服务是先查询context中的mServiceCache,如果没有service的cache,则重新create一下。
  4. 重新create就分情况了,如果是AccessibilityManager这样的单例的manager对象还好,不会持有传入的context引用以存到context的mServiceChache中;如果是WifiManager,则传入多少个不同的context,那么就有多少个context的mServiceCache中包含WifiManager,如果WifiManager不释放,那么就导致内存泄露了。

PS:不同的contextImpl都会从SystemServiceRegistry中new出一个新的Object数组,是不共享的。

    ContextImpl.java// The system service cache for the system services that are cached per-ContextImpl.final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();SystemServiceRegistry.java/*** Creates an array which is used to cache per-Context service instances.*/public static Object[] createServiceCache() {return new Object[sServiceCacheSize];}

 

2. 总结

一个应用的static变量还是和applicationContext更配一点,毕竟生命周期差不多,如果调用getSystemService,也不用管manager是单例的还是重新new的,传入applicationContext比较好。

 

 

3.疑问

getOutContext和普通的ctx有什么区别?是applicationContext么?

        registerService(Context.DISPLAY_SERVICE, DisplayManager.class,new CachedServiceFetcher<DisplayManager>() {@Overridepublic DisplayManager createService(ContextImpl ctx) {return new DisplayManager(ctx.getOuterContext());}});

看了下OuterContext是在activity创建的时候初始化的,就是activity,appContext对应与activity 的服务端contextImpl。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}ContextImpl appContext = createBaseContextForActivity(r);Activity activity = null;try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (localLOGV) Slog.v(TAG, "Performing launch of " + r);if (localLOGV) Slog.v(TAG, r + ": app=" + app+ ", appName=" + app.getPackageName()+ ", pkg=" + r.packageInfo.getPackageName()+ ", comp=" + r.intent.getComponent().toShortString()+ ", dir=" + r.packageInfo.getAppDir());if (activity != null) {CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mCompatConfiguration);if (r.overrideConfig != null) {config.updateFrom(r.overrideConfig);}if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);Window window = null;if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {window = r.mPendingRemoveWindow;r.mPendingRemoveWindow = null;r.mPendingRemoveWindowManager = null;}appContext.setOuterContext(activity);
。。。

搜了下contextImpl初始化的时候会将outerContext初始化为自己,此外还有如下会重新赋值:

./base/core/java/android/app/LoadedApk.java:998:            appContext.setOuterContext(app);
./base/core/java/android/app/ActivityThread.java:2780:                appContext.setOuterContext(activity);
./base/core/java/android/app/ActivityThread.java:3371:                    context.setOuterContext(agent);
./base/core/java/android/app/ActivityThread.java:3445:            context.setOuterContext(service);
./base/core/java/android/app/ContextImpl.java:2404:    final void setOuterContext(Context context) {

 

这篇关于(七十七)getSystemService内存泄露探讨的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511425

相关文章

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

Redis多种内存淘汰策略及配置技巧分享

《Redis多种内存淘汰策略及配置技巧分享》本文介绍了Redis内存满时的淘汰机制,包括内存淘汰机制的概念,Redis提供的8种淘汰策略(如noeviction、volatile-lru等)及其适用场... 目录前言一、什么是 Redis 的内存淘汰机制?二、Redis 内存淘汰策略1. pythonnoe

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少

JVM 常见异常及内存诊断

栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存。 栈帧过多导致栈内存溢出 下述示例代码,由于递归深度没有限制且没有设置出口,每次方法的调用都会产生一个栈帧导致了创建的栈帧过多,而导致内存溢出(StackOverflowError)。 示例代码: 运行结果: 栈帧过大导致栈内存