美文网首页Android技术知识Android开发Android开发经验谈
由于频繁GC造成的界面卡顿原因分析

由于频繁GC造成的界面卡顿原因分析

作者: luckyyyyou | 来源:发表于2018-05-22 17:46 被阅读0次

对于GC这个近乎玄学的东西一直是感觉神龙见首不见尾,看得见但是摸不着,Monitor里面每次都有它,一切看起来都似乎是理所当然地进行着,对GC的印象还是一直停留在一个自动运转的垃圾回收器默默地帮我处理好了关于内存分配和回收的一切,虽然事实上GC的作用就是这些,但是当碰到界面卡顿的时候不得不怀疑是过于频繁地GC所造成的。


1.数据库:怪我咯?

我们知道,一切涉及到数据库的操作都比较耗费系统的性能,因为实质上它是一个不断I/O的过程,始终伴随着变量的生成和回收,所以操作过于频繁的时候或者当数据库过于庞大的时候这个过程就会因为过于频繁地GC而造成界面卡顿。

2.实例分析

需求:用户点击查询某一天的历史数据信息,判断本地数据库中是否存在数据:

    List<Datalog> datalogs = DataSupport.findAll(Datalog.class);
        if (datalogs.size() > 0){
            //TODO
        }

继续看findAll()源码,发现是个同步锁方法

    public static synchronized <T> List<T> findAll(Class<T> modelClass, long... ids) {
        return findAll(modelClass, false, ids);
    }
    public static synchronized <T> List<T> findAll(Class<T> modelClass, boolean isEager,
            long... ids) {
        QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
        return queryHandler.onFindAll(modelClass, isEager, ids);
    }
    <T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {
        List<T> dataList;
        if (isAffectAllLines(ids)) {
            dataList = query(modelClass, null, null, null, null, null, "id", null,
                    getForeignKeyAssociations(modelClass.getName(), isEager));
        } else {
            dataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, "id",
                    null, getForeignKeyAssociations(modelClass.getName(), isEager));
        }
        return dataList;
    }

这边是通过query()来返回一个数据集合。

            if (cursor.moveToFirst()) {
                SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<QueryInfoCache>();
                Map<Field, GenericModel> genericModelMap = new HashMap<Field, GenericModel>();
                do {
                    T modelInstance = (T) createInstanceFromClass(modelClass);
                    giveBaseObjIdValue((DataSupport) modelInstance,
                            cursor.getLong(cursor.getColumnIndexOrThrow("id")));
                    setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
                    setGenericValueToModel((DataSupport) modelInstance, supportedGenericFields, genericModelMap);
                    if (foreignKeyAssociations != null) {
                        setAssociatedModel((DataSupport) modelInstance);
                    }
                    dataList.add(modelInstance);
                } while (cursor.moveToNext());
                queryInfoCacheSparseArray.clear();
                genericModelMap.clear();
            }

可见整个数据库操作的时间和数据库的大小也就是数据量是有直接关系的,有几条数据就会产生几个数据单例,当数据量过大的时候就会造成由于频繁地GC而造成的界面卡顿效果,下面Monitor调试监控情况:

logcat
Monitors
很明显,在用户点击查询之后由于进行了多达8次的GC过程,CPU地使用率一度高达50%,在ANR的边缘不断试探,界面在短时间内处于不可操作的状态,加载对话框也无法正常显示,用户体验这时候是极差的,由源码我们知道就是因为在初始化dataList的时候由于数据量过于庞大造成的频繁GC的结果。
简而言之, 就是执行GC操作的时候,任何线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行, 故而如果程序频繁GC, 自然会导致界面卡顿。

3.解决方法

由于在历史数据库中只存在某一天的数据,所以在判断数据中是否存在被查询的数据时是需要查询数据库中的第一条数据的时间是否符合要求:

        /*
        取得数据库第一条数据的年月日
         */
        Datalog firstLog = DataSupport.findFirst(Datalog.class);
        if (firstLog != null){
            String time = firstLog.getDtime();
            String checkTime = time.substring(0,8);
            if (date.substring(2,10).equals(checkTime)){
                Log.d("初始化阶段","搜索数据库");
                /*
                读数据库,显示数据
                 */
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        searchAndShowData();
                    }
                }).start();
            }else {
                Log.d("初始化阶段","无该日数据-请求服务器");
                DataSupport.deleteAll(Datalog.class);
                //解析用户输入的年月日
                parseAndQueryServer(date);
            }
        }else {
            //解析用户输入的年月日
            Log.d("初始化阶段","无数据-请求服务器");
            DataSupport.deleteAll(Datalog.class);
            parseAndQueryServer(date);
        }
    <T> T onFindFirst(Class<T> modelClass, boolean isEager) {
        List<T> dataList = query(modelClass, null, null, null, null, null, "id", "1",
                getForeignKeyAssociations(modelClass.getName(), isEager));
        if (dataList.size() > 0) {
            return dataList.get(0);
        }
        return null;
    }

onFindFirst()方法里只需要查询一条数据,这就意味着while只会循环一次,大大减少了GC的时间和次数。
改进之后的效果:

logcat
GC只进行了一次,界面卡顿问题完美解决。

相关文章

  • 由于频繁GC造成的界面卡顿原因分析

    对于GC这个近乎玄学的东西一直是感觉神龙见首不见尾,看得见但是摸不着,Monitor里面每次都有它,一切看起来都似...

  • 内存抖动

    造成内存抖动的原因。 频繁大量的创建对象,造成虚拟机频繁触发GC。 内存抖动有什么后果。 1.程序卡顿;2.可能回...

  • Android 性能优化系列 - 03 使用对象池优化内存

    一. 概述 有时候 UI 卡顿是因为发生了频繁的 GC 造成的,频繁的 GC 其实对应的内存情况就是内存抖动,而发...

  • 自定义View-性能优化

    1.内存优化 如果内存使用较多或者存在内存泄漏,导致系统GC频繁,从而造成页面卡顿,造成页面卡顿;如果绘制时消耗时...

  • iOS 底层原理:界面优化

    界面优化无非就是解决卡顿问,优化界面流畅度,以下就通过先分析卡顿的原因,然后再介绍具体的优化方案,来分析如何做界面...

  • iOS 底层原理:界面优化

    界面优化无非就是解决卡顿问,优化界面流畅度,以下就通过先分析卡顿的原因,然后再介绍具体的优化方案,来分析如何做界面...

  • android性能优化总结

    1.绘制优化 卡顿造成原因: 界面绘制:①页面复杂,层级较深,造成measure,layout,draw都有可能耗...

  • 卡顿监控

    卡顿 3.卡顿监控 卡顿:卡顿阀值设置为主线程执行消息队列中的消息时间超过200ms认为主线程卡顿频繁GC操作也会...

  • 内存优化——内存抖动

    内存抖动是指内存频繁地分配和回收,而频繁的gc会导致卡顿,严重时和内存泄漏一样会导致OOM。内存抖动为什么会造成O...

  • WPF中UI线程频繁操作造成卡顿的处理(一)

    转载请注明原作者 目录 WPF中UI线程频繁操作造成卡顿的处理(一)WPF中UI线程频繁操作造成卡顿的处理(二) ...

网友评论

    本文标题:由于频繁GC造成的界面卡顿原因分析

    本文链接:https://www.haomeiwen.com/subject/kptkjftx.html