由于頻繁GC造成的界面卡頓原因分析


對于GC這個近乎玄學(xué)的東西一直是感覺神龍見首不見尾,看得見但是摸不著睦焕,Monitor里面每次都有它炕贵,一切看起來都似乎是理所當(dāng)然地進行著,對GC的印象還是一直停留在一個自動運轉(zhuǎn)的垃圾回收器默默地幫我處理好了關(guān)于內(nèi)存分配和回收的一切茧吊,雖然事實上GC的作用就是這些,但是當(dāng)碰到界面卡頓的時候不得不懷疑是過于頻繁地GC所造成的八毯。


1.數(shù)據(jù)庫:怪我咯搓侄?

我們知道,一切涉及到數(shù)據(jù)庫的操作都比較耗費系統(tǒng)的性能话速,因為實質(zhì)上它是一個不斷I/O的過程讶踪,始終伴隨著變量的生成和回收,所以操作過于頻繁的時候或者當(dāng)數(shù)據(jù)庫過于龐大的時候這個過程就會因為過于頻繁地GC而造成界面卡頓泊交。

2.實例分析

需求:用戶點擊查詢某一天的歷史數(shù)據(jù)信息乳讥,判斷本地數(shù)據(jù)庫中是否存在數(shù)據(jù):

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

繼續(xù)看findAll()源碼,發(fā)現(xiàn)是個同步鎖方法

    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()來返回一個數(shù)據(jù)集合廓俭。

            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();
            }

可見整個數(shù)據(jù)庫操作的時間和數(shù)據(jù)庫的大小也就是數(shù)據(jù)量是有直接關(guān)系的云石,有幾條數(shù)據(jù)就會產(chǎn)生幾個數(shù)據(jù)單例,當(dāng)數(shù)據(jù)量過大的時候就會造成由于頻繁地GC而造成的界面卡頓效果研乒,下面Monitor調(diào)試監(jiān)控情況:

logcat

Monitors

很明顯汹忠,在用戶點擊查詢之后由于進行了多達8次的GC過程,CPU地使用率一度高達50%,在ANR的邊緣不斷試探宽菜,界面在短時間內(nèi)處于不可操作的狀態(tài)奖地,加載對話框也無法正常顯示,用戶體驗這時候是極差的赋焕,由源碼我們知道就是因為在初始化dataList的時候由于數(shù)據(jù)量過于龐大造成的頻繁GC的結(jié)果参歹。
簡而言之, 就是執(zhí)行GC操作的時候,任何線程的任何操作都會需要暫停隆判,等待GC操作完成之后犬庇,其他操作才能夠繼續(xù)運行, 故而如果程序頻繁GC, 自然會導(dǎo)致界面卡頓。

3.解決方法

由于在歷史數(shù)據(jù)庫中只存在某一天的數(shù)據(jù)侨嘀,所以在判斷數(shù)據(jù)中是否存在被查詢的數(shù)據(jù)時是需要查詢數(shù)據(jù)庫中的第一條數(shù)據(jù)的時間是否符合要求:

        /*
        取得數(shù)據(jù)庫第一條數(shù)據(jù)的年月日
         */
        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("初始化階段","搜索數(shù)據(jù)庫");
                /*
                讀數(shù)據(jù)庫臭挽,顯示數(shù)據(jù)
                 */
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        searchAndShowData();
                    }
                }).start();
            }else {
                Log.d("初始化階段","無該日數(shù)據(jù)-請求服務(wù)器");
                DataSupport.deleteAll(Datalog.class);
                //解析用戶輸入的年月日
                parseAndQueryServer(date);
            }
        }else {
            //解析用戶輸入的年月日
            Log.d("初始化階段","無數(shù)據(jù)-請求服務(wù)器");
            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()方法里只需要查詢一條數(shù)據(jù),這就意味著while只會循環(huán)一次咬腕,大大減少了GC的時間和次數(shù)欢峰。
改進之后的效果:

logcat

GC只進行了一次,界面卡頓問題完美解決涨共。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纽帖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子举反,更是在濱河造成了極大的恐慌懊直,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件火鼻,死亡現(xiàn)場離奇詭異室囊,居然都是意外死亡,警方通過查閱死者的電腦和手機魁索,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門融撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粗蔚,你說我怎么就攤上這事尝偎。” “怎么了支鸡?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵冬念,是天一觀的道長。 經(jīng)常有香客問我牧挣,道長,這世上最難降的妖魔是什么醒陆? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任瀑构,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寺晌。我一直安慰自己世吨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布呻征。 她就那樣靜靜地躺著耘婚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陆赋。 梳的紋絲不亂的頭發(fā)上沐祷,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音攒岛,去河邊找鬼赖临。 笑死,一個胖子當(dāng)著我的面吹牛灾锯,可吹牛的內(nèi)容都是我干的兢榨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼顺饮,長吁一口氣:“原來是場噩夢啊……” “哼吵聪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兼雄,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤暖璧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后君旦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澎办,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年金砍,在試婚紗的時候發(fā)現(xiàn)自己被綠了局蚀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡恕稠,死狀恐怖琅绅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹅巍,我是刑警寧澤千扶,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站骆捧,受9級特大地震影響澎羞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敛苇,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一妆绞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦括饶、人聲如沸株茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽启盛。三九已至,卻和暖如春技羔,著一層夾襖步出監(jiān)牢的瞬間僵闯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工堕阔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棍厂,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓超陆,卻偏偏與公主長得像牺弹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子时呀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,757評論 25 707
  • 在一個方法內(nèi)部定義的變量都存儲在棧中张漂,當(dāng)這個函數(shù)運行結(jié)束后,其對應(yīng)的棧就會被回收谨娜,此時航攒,在其方法體中定義的變量將不...
    Y了個J閱讀 4,413評論 1 14
  • Java Virtual Machine簡寫JVM。通過針對不同系統(tǒng)趴梢、硬件平臺實現(xiàn)統(tǒng)一的應(yīng)用接口漠畜,是Java語言跨...
    Java技術(shù)進階閱讀 288評論 0 0
  • 2018.2.28.星期三.小雨轉(zhuǎn)陰 今天早上醒來就七點了,昨天晚上睡的晚坞靶,又習(xí)慣性的半夜醒來憔狞,導(dǎo)致今天晚點...
    范曉雪媽媽閱讀 159評論 0 0
  • 大概是好友昨天去健身了 讓我總以為今天是周六 于是 窩宿舍整整一天 可能厭學(xué)了吧 明明結(jié)課考試快來了 卻毫無斗志 ...
    啊布要減肥閱讀 182評論 2 1