1、问题描述

旋转屏测试5~6小时system_server发生abort,手机重启导致测试停止,android7.0平台多个项目都有此问题爆出。

和测试同事了解旋转屏幕在settings界面进行操作概率较高。

2、问题分析

从tombstone分析system_server abort原因是global reference table overflow 。

pid: 3749, tid: 8164, name:Binder:3749_E  >>> system_server<<<

signal 6 (SIGABRT), code -6 (SI_TKILL), faultaddr --------

Abort message: 'vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]JNI ERROR (app bug): global reference table overflow (max=51200)'

rax0000000000000000  rbx00007f0567b034f8  rcx00007f0590c28f37  rdx 0000000000000006

rsi0000000000001fe4  rdi 0000000000000ea5

r8  ffffffffffffffd8  r9 00007f0567b01790  r10 0000000000000008  r11 0000000000000206

r120000000000001fe4  r130000000000000006  r1400007f0567b024b0  r15 00007f05902d0800

cs  0000000000000033  ss 000000000000002b

rip00007f0590c28f37  rbp000000000000000b  rsp00007f0567b02358  eflags 0000000000000206

backtrace:

#00pc 000000000008ef37 /system/lib64/libc.so (tgkill+7)

#01pc 000000000008b952 /system/lib64/libc.so (pthread_kill+66)

#02pc 0000000000030371  /system/lib64/libc.so(raise+17)

#03pc 000000000002875e /system/lib64/libc.so (abort+78)

#04pc 0000000000572f29 /system/lib64/libart.so (_ZN3art7Runtime5AbortEv+361)

#05pc 00000000001d2091 /system/lib64/libart.so (_ZN3art10LogMessageD1Ev+817)

#06pc 000000000036b5dd /system/lib64/libart.so(_ZN3art22IndirectReferenceTable3AddEjPNS_6mirror6ObjectE+669)

#07pc 00000000004201e9 /system/lib64/libart.so(_ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE+57)

#08pc 000000000045cd3b /system/lib64/libart.so

此类泄漏问题可能是由于naitve层代码导致也可能是java层代码导致,需要具体分析log。

A001-01 23:45:55.469  3749 8164 F art     : vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]      6202 of com.android.server.print.UserState$4 (6202 unique instances)

A001-01 23:45:55.469  3749 8164 F art     :vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]      6198 of com.android.server.print.UserState$3 (6198 unique instances)

A001-01 23:45:55.465  3749 8164 F art     :vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]     18626 of android.os.RemoteCallbackList$Callback (18626 unique instances)

A001-01 23:45:55.463  3749 8164 F art     :vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]     19147 of java.lang.ref.WeakReference(19147 unique instances)

18626 + 19147+ 6202 + 6198  = 50173

从log看是上面4个类型对象实例过多导致,这里4个对象的引用和是50173,加上其他的对象引用就大于了我们设置的阀值51200,就会主动触发abort。

从上面log我们不能完全确定是那里的代码逻辑有问题,因为UserState、RemoteCallbackList、WeakReference都是比较公共的类。我们需要通过发生问题时的调用栈来看是哪里有问题。

先看RemoteCallbackList相关调用栈如下

A001-01 23:45:55.381  3749 8164 E         : native trace:

A001-01 23:45:55.381  3749 8164 E         : #00 pc000000000045cd3b  /system/lib64/libart.so(_ZN3art3JNI12NewGlobalRefEP7_JNIEnvP8_jobject+267)

A001-01 23:45:55.381  3749 8164 E         : #01 pc000000000010f303  /system/lib64/libandroid_runtime.so

A001-01 23:45:55.381  3749 8164 E         : #02 pc000000000004ef8b /data/dalvik-cache/x86_64/system@framework@boot.oat (offset 0x2c30000)

A001-01 23:45:55.381  3749 8164 E         : java trace:

A001-01 23:45:55.381  3749 8164 E         :   at android.os.BinderProxy.linkToDeath(Nativemethod)

A001-01 23:45:55.381  3749 8164 E         :   atandroid.os.RemoteCallbackList.register(RemoteCallbackList.java:114)

A001-01 23:45:55.381  3749 8164 E         :   - locked <0x0fcaf6ec> (a android.util.ArrayMap)

A001-01 23:45:55.381  3749 8164 E         :   atandroid.os.RemoteCallbackList.register(RemoteCallbackList.java:78)

A001-01 23:45:55.381  3749 8164 E         :   atcom.android.server.wallpaper.WallpaperManagerService.getWallpaper(WallpaperManagerService.java:1276)

A001-01 23:45:55.381  3749 8164 E         :   - locked <0x072f3ab5> (ajava.lang.Object)

A001-01 23:45:55.381  3749 8164 E         :   atandroid.app.IWallpaperManager$Stub.onTransact(IWallpaperManager.java:127)

A001-01 23:45:55.381  3749 8164 E         :   atandroid.os.Binder.execTransact(Binder.java:591)

由于RemoteCallbackList相关的引用有18626,所以我们先来处理这个

从上面的调用栈看可以确认是settings与system_server进行binder通信,而binder通信过程中system_server的RemoteCallbackList实例是通过getWallpaper注册的,这里实例多通过代码分析是settings中转屏会调用hasWallpaperSet,这里会和WallpaperManagerService进行binder通信,注册对应的callBack实例,由于长时间转屏,就会导致实例堆积没有释放,进而越来越多。对应代码如下

settings中在调用异步线程后台执行会调用isSuggestionComplete,这里会触发getWallpaper注册RemoteCallback

DashboardSummary.java

269       protected List<Tile> doInBackground(Void... params) {

270           List<Tile> suggestions = mSuggestionParser.getSuggestions();

271           for (int i = 0; i < suggestions.size(); i++) {

272                if (mSuggestionsChecks.isSuggestionComplete(suggestions.get(i))){

273                   mAdapter.disableSuggestion(suggestions.get(i));

274                    suggestions.remove(i--);

275                }

276           }

packages/apps/Settings/src/com/android/settings/dashboard/SuggestionsChecks.java

99   private boolean hasWallpaperSet() {

100       IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);

101       IWallpaperManager service = Stub.asInterface(b);

102       try {

103           return service.getWallpaper(mCallback, WallpaperManager.FLAG_SYSTEM,

104                    new Bundle(),mContext.getUserId()) != null;

105       } catch (RemoteException e) {

106       }

这里有定义对应的callback实例

110   private final IWallpaperManagerCallback mCallback = new  IWallpaperManagerCallback.Stub() ......

server 端,当cb不为null时会注册cb函数,cb实例过多导致RemoteCallbackList过多
/frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
1248    public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, final int which,
1249            Bundle outParams, int wallpaperUserId) {
......
1275                if (cb != null) {
1276                    wallpaper.callbacks.register(cb);
1277                }

这里我们找到了第一个实例过多的代码,从代码看是google 原生的代码,这里的代码存在漏洞,如果长时间转屏就会导致系统发生实例过多消耗过多的内存,造成泄漏。这里修改方法有多种不做详细赘述。

下面我们来看第二类UserState实例多问题,看下我们打印的调用栈

A001-01 23:45:55.469  3749 8164 F art     : vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]      6202 of com.android.server.print.UserState$4 (6202 unique instances)
A001-01 23:45:55.469  3749 8164 F art     :vendor/intel/art-extension/runtime/indirect_reference_table.cc:125]      6198 of com.android.server.print.UserState$3 (6198 unique instances)

从上面log可以知道这里userState实例有两类

A001-02 09:28:10.277  3414  3414 E         : native trace:

A001-02 09:28:10.277  3414  3414 E         : #00 pc 000000000045cd2b  /system/lib64/libart.so (_ZN3art3JNI12NewGlobalRefEP7_JNIEnvP8_jobject+267)
A001-02 09:28:10.277  3414  3414 E         : #01 pc 000000000010f383  /system/lib64/libandroid_runtime.so  android_util_Binder.cpp incRefsCreated
A001-02 09:28:10.277  3414  3414 E         : #02 pc 000000000004ef8b  /data/dalvik-cache/x86_64/system@framework@boot.oat (offset 0x2c34000)
A001-02 09:28:10.277  3414  3414 E         : java trace:
A001-02 09:28:10.277  3414  3414 E         :   at android.os.BinderProxy.linkToDeath(Native method)
A001-02 09:28:10.277  3414  3414 E         :   at com.android.server.print.UserState$ListenerRecord.<init>(UserState.java:1248)
A001-02 09:28:10.277  3414  3414 E         :   at com.android.server.print.UserState$4.<init>(UserState.java:597)
A001-02 09:28:10.277  3414  3414 E         :   at com.android.server.print.UserState.addPrintServicesChangeListener(UserState.java:597)
A001-02 09:28:10.277  3414  3414 E         :   - locked <0x057d2eee> (a java.lang.Object)
A001-02 09:28:10.277  3414  3414 E         :   at com.android.server.print.PrintManagerService$PrintManagerImpl.addPrintServicesChangeListener(PrintManagerService.java:553)
A001-02 09:28:10.277  3414  3414 E         :   at android.print.IPrintManager$Stub.onTransact(IPrintManager.java:185)

A001-02 09:28:10.277  3414  3414 E         :   at android.os.Binder.execTransact(Binder.java:591)

A001-02 09:28:10.278  3414  3414 E         : native trace:
A001-02 09:28:10.278  3414  3414 E         : #00 pc 000000000045cd2b  /system/lib64/libart.so (_ZN3art3JNI12NewGlobalRefEP7_JNIEnvP8_jobject+267)
A001-02 09:28:10.278  3414  3414 E         : #01 pc 000000000010f383  /system/lib64/libandroid_runtime.so
A001-02 09:28:10.278  3414  3414 E         : #02 pc 000000000004ef8b  /data/dalvik-cache/x86_64/system@framework@boot.oat (offset 0x2c34000)
A001-02 09:28:10.278  3414  3414 E         : java trace:
A001-02 09:28:10.278  3414  3414 E         :   at android.os.BinderProxy.linkToDeath(Native method)
A001-02 09:28:10.278  3414  3414 E         :   at com.android.server.print.UserState$PrintJobStateChangeListenerRecord.<init>(UserState.java:1231)
A001-02 09:28:10.278  3414  3414 E         :   at com.android.server.print.UserState$3.<init>(UserState.java:555)
A001-02 09:28:10.278  3414  3414 E         :   at com.android.server.print.UserState.addPrintJobStateChangeListener(UserState.java:555)
A001-02 09:28:10.278  3414  3414 E         :   - locked <0x057d2eee> (a java.lang.Object)
A001-02 09:28:10.278  3414  3414 E         :   at com.android.server.print.PrintManagerService$PrintManagerImpl.addPrintJobStateChangeListener(PrintManagerService.java:509)
A001-02 09:28:10.278  3414  3414 E         :   at android.print.IPrintManager$Stub.onTransact(IPrintManager.java:163)
A001-02 09:28:10.278  3414  3414 E         :   at android.os.Binder.execTransact(Binder.java:591)

从调用栈信息可知还是settings与system_server有binder通信导致,system_server中积累了大量的实例对象。那么这是哪里导致的实例多的问题呢?

插播小知识:UserState$3、UserState$4这里UserState类后面跟$符加数字表示UserState类中包含的内部类,UserState类序号是1,之后的内部类是2、3、4等。代码参见frameworks\base\services\print\java\com\android\server\print\UserState.java

这样我们根据序号也可以确定是调用的那个内部类中的方法。

下面结合代码分析,先分析UserState$3的代码

packages/apps/Settings/src/com/android/settings/print/PrintJobSettingsFragment.java

52    private final PrintJobStateChangeListener mPrintJobStateChangeListener =
53            new PrintJobStateChangeListener() {
54        @Override
55        public void onPrintJobStateChanged(PrintJobId printJobId) {
56            updateUi();
57        }

58    };

packages/apps/Settings/src/com/android/settings/print/PrintJobSettingsFragment.java
98    public void onStart() {
99        super.onStart();
100        mPrintManager.addPrintJobStateChangeListener(
101                mPrintJobStateChangeListener);
102        updateUi();

103    }

之后会调用

frameworks/base/core/java/android/print/PrintManager.java
    public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
        if (mService == null) {
            Log.w(LOG_TAG, "Feature android.software.print not available");
            return;
        }
        if (mPrintJobStateChangeListeners == null) {
            mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener,
                    PrintJobStateChangeListenerWrapper>();
        }
//初始化对应的listener, mHandler
        PrintJobStateChangeListenerWrapper wrappedListener =
                new PrintJobStateChangeListenerWrapper(listener, mHandler);
        try {
            mService.addPrintJobStateChangeListener(wrappedListener, mAppId, mUserId);

mPrintJobStateChangeListeners.put(listener, wrappedListener);

接下来调用下面代码

frameworks/base/services/print/java/com/android/server/print/PrintManagerService.java

public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,

int appId, int userId) throws RemoteException {
            listener = Preconditions.checkNotNull(listener);
            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
            final int resolvedAppId;
            final UserState userState;
            synchronized (mLock) {
                // Only the current group members can add a print job listener.
                if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
                    return;
                }
                resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
                userState = getOrCreateUserStateLocked(resolvedUserId, false);
            }
            final long identity = Binder.clearCallingIdentity();
            try {
                userState.addPrintJobStateChangeListener(listener, resolvedAppId);
            } finally {
                Binder.restoreCallingIdentity(identity);

}

最终调用到UserState类中的addPrintJobStateChangeListener,也就是我们出问题时看到的调用栈
frameworks/base/services/print/java/com/android/server/print/UserState.java
546    public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
547            int appId) throws RemoteException {
548        synchronized (mLock) {
549            throwIfDestroyedLocked();
550            if (mPrintJobStateChangeListenerRecords == null) {
551                mPrintJobStateChangeListenerRecords =
552                        new ArrayList<PrintJobStateChangeListenerRecord>();
553            }
554            mPrintJobStateChangeListenerRecords.add(
555                    new PrintJobStateChangeListenerRecord(listener, appId) {//这里会new对应的PrintJobStateChangeListenerRecord
556                @Override
557                public void onBinderDied() {//这里是当binder端发生死亡通知时这里会执行remove掉对应的listener
558                    synchronized (mLock) {
559                        if (mPrintJobStateChangeListenerRecords != null) {
560                            mPrintJobStateChangeListenerRecords.remove(this);
561                        }

562                    }

通过UserState类中的PrintJobStateChangeListenerRecord内部类可以看出,这里有注册一个死亡通知链linkToDeath

frameworks/base/services/print/java/com/android/server/print/UserState.java

private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
        @NonNull final IPrintJobStateChangeListener listener;
        final int appId;

public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener,
                int appId) throws RemoteException {
            this.listener = listener;
            this.appId = appId;
            listener.asBinder().linkToDeath(this, 0);//这里底层会incWeakrefs
        }
        @Override
        public void binderDied() {
            listener.asBinder().unlinkToDeath(this, 0);
            onBinderDied();
        }
        public abstract void onBinderDied();

}

static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
        jobject recipient, jint flags) // throws RemoteException
{
......

sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);//这里会调用env->NewGlobalRef(object)建立引用

frameworks/base/core/jni/android_util_Binder.cpp
370    JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
371        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
372          mObjectWeak(NULL), mList(list)
373    {
374        // These objects manage their own lifetimes so are responsible for final bookkeeping.
375        // The list holds a strong reference to this object.
376        LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
377        list->add(this);
378
379        android_atomic_inc(&gNumDeathRefs);
380        incRefsCreated(env);
381    }

从上面的代码流程可以看出,当settings在旋转屏是会启动print相关服务注册一个listener,这个linstener只有在binderDied中调用onBinderDied时才会remove对应的listener,而在旋转屏幕过程中这个组测的listener明显是不会被remove掉,所以长时间旋转这里就会导致注册多个listener,一直不释放就会导致system_server中的GlobalRef对象增多,只要测试时间够长就会引发当前问题。此流程为google原生。修改就是及时remove掉通过注册listener所增加的GlobalRef对象就可以解决。

UserState$4的代码流程和UserState$3的流程和问题点一样,修改方法相同,代码如下

frameworks/base/core/java/android/print/PrintServicesLoader.java
79    protected void onStartLoading() {
80        mHandler = new MyHandler();
81        mListener = new PrintManager.PrintServicesChangeListener() {
82            @Override public void onPrintServicesChanged() {
83                queueNewResult();
84            }
85        };
86
87        mPrintManager.addPrintServicesChangeListener(mListener);
88
89        // Immediately deliver a result
90        deliverResult(mPrintManager.getPrintServices(mSelectionFlags));
91    }

frameworks/base/core/java/android/print/PrintManager.java
573    void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
574        Preconditions.checkNotNull(listener);
575
576        if (mService == null) {
577            Log.w(LOG_TAG, "Feature android.software.print not available");
578            return;
579        }
580        if (mPrintServicesChangeListeners == null) {
581            mPrintServicesChangeListeners = new ArrayMap<>();
582        }
583        PrintServicesChangeListenerWrapper wrappedListener =
584                new PrintServicesChangeListenerWrapper(listener, mHandler);
585        try {
586            mService.addPrintServicesChangeListener(wrappedListener, mUserId);
587            mPrintServicesChangeListeners.put(listener, wrappedListener);
588        } catch (RemoteException re) {
589            throw re.rethrowFromSystemServer();
590        }
591    }

frameworks/base/services/print/java/com/android/server/print/UserState.java
589    public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
590            throws RemoteException {
591        synchronized (mLock) {
592            throwIfDestroyedLocked();
593            if (mPrintServicesChangeListenerRecords == null) {
594                mPrintServicesChangeListenerRecords = new ArrayList<>();
595            }
596            mPrintServicesChangeListenerRecords.add(
597                    new ListenerRecord<IPrintServicesChangeListener>(listener) {
598                        @Override
599                        public void onBinderDied() {
600                            synchronized (mLock) {
601                                if (mPrintServicesChangeListenerRecords != null) {
602                                    mPrintServicesChangeListenerRecords.remove(this);
603                                }
604                            }
605                        }
606                    });

修改完前三个问题后,进行复测,问题不复现。至此问题解决。

【泄漏类】android7.0 system_server发生global reference table overflow问题分析相关推荐

  1. local reference table overflow 内存泄露

    local reference table overflow JNI层coding经常会遇到ReferenceTable overflow问题,特别是当jni函数被反复调用上千上万次的时候,现汇总如下 ...

  2. Android稳定性系列9 global reference限制策略

    一 概述 global reference使用不当,就会引发lobal reference overflow异常问题,为了解决这个问题,从Android 9.0开始新增了限制策略. 先来看看虚拟机的一 ...

  3. Android7.0反射类找不到的问题

    Java中使用反射的地方较多,尤其是各种框架中.最近在Android7.0的项目中遇到个问题很奇怪,反射使用的类找不到了,但是编译的时候没问题啊.然后在代码中使用非反射的方式调用代码也是没有问题的,这 ...

  4. Android7.0 发生的android.os.FileUriExposedException错误

    在Android7.0的系统上调用系统相机拍照或者进相册,便会出现android.os.FileUriExposedException错误.这是安卓7.0以上版本,做了一些系统权限更改,为了提高私有文 ...

  5. Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)

    Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...

  6. Android7.0适配方案

    1安装时解析错误 我们的App通常会有检查更新的功能.用户在收到提示更新并且下载完后,会自动打开安装页面让用户来去安装.这时就会出现安装错误的问题,这类的问题的可能性比较多.比如较低版本的App想要覆 ...

  7. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  8. Android7.0关机流程分析

    在长按power键时系统会弹出对话框,让用户选择关机, 重启或者其他模式. 在本文中重点讲解系统关机流程. 让大家了解在系统关机过程都做了哪些事情,而导致关机慢又有那些主要的原因.在Android7. ...

  9. Android7.0以上多系统语言的国际化适配

    前言 近期偶然发现一个问题,我们的应用在7.0以上的个别机型上,会遇到国际化不对的问题,现象是:手机明明设置了中文,应用却可能显示成英文. 问题分析 问题机型:三星s8 plus 系统版本:Andro ...

最新文章

  1. Business Contact Mnanager for Outlook2010之二:常用基本功能
  2. [导入]web2.0中流行的设计元素:颜色
  3. 使用freemarker生成xml模板
  4. ci github 通知_GitHub 欢迎一切 CI 工具
  5. bxl类型封装转换为AD库封装
  6. vb连接oracle 工程,VB 连接Oracle数据库
  7. CIKM 2020 | FANG:利用社会语境及其图表示进行假新闻检测
  8. PCIe扫盲——PCI总线的三种传输模式
  9. 图-介绍(intro-Graph)
  10. js中文汉字按拼音排序
  11. mysql的binlog太大太多占用大量磁盘的解决
  12. java无法解析zip
  13. 安卓培训机构排名!这篇文章可以满足你80%日常工作!跳槽薪资翻倍
  14. 【Autopsy数字取证篇】Autopsy案例创建与镜像分析详细教程
  15. 自动在副屏/虚拟屏启动100%鲜橙汁 | 可用于上班摸鱼
  16. FPGA信号处理--多相滤波器(二)
  17. Restful风格设计
  18. PDF转CAD软件使用的便捷操作步骤
  19. [VisionPro] C#脚本介绍
  20. Mac——技巧:用“提醒事项”将任务分配给其他人

热门文章

  1. 【安卓小程序】app 首页
  2. 【深度学习】:非极大值抑制(NMS)详解
  3. 【CSDN竞赛第25期】赢热门图书《千脑智能》和定制周边
  4. GOM引擎传奇中增加会员时间的脚本教程分享
  5. 游戏引擎Easy2D-教程-简单绘画-蜡笔(基于版本V2.1.14)
  6. python matplotlib pyplot方法_Matplotlib.pyplot 常用方法
  7. java javax org_java mail程序--ClassNotFoundException:javax.mail.Address
  8. python调用大漠getcursorpos,GetCursorPos()函数
  9. Oracle 函数使用:CURSOR游标简单案例
  10. squid SwitchyOmega 实现浏览器代理切换