• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 安卓學習筆記之內存優化(一)

    標簽: java  內存  性能優化  android

    一個良好的App是經過嚴格的性能優化和內存優化給用戶帶來良好的操作。今天就說一下內存優化。

    Java四種引用

    Java的四種引用方式。

    • 強引用 無論內存充足與否,系統都不會銷毀對象實例。
    • 弱引用 只要產生了GC(垃圾回收器),弱引用實例對象容易被銷毀。
    • 虛引用 檢測對象是否已經回收
    • 軟引用 只要內存不足,就會被釋放

    通過代碼來演示一下效果。

    public  static  void main(String[] args){
    
            //強引用 內存無論怎么樣系統都不會釋放
            String str=new String("String");
    
            //軟引用 只要內存不足就釋放掉
            SoftReference<String> softReference=new SoftReference<String>(str);
    
            //弱引用 只要系統產生了GC(垃圾回收)它引用的對象就會被釋放掉
            WeakReference<String> weakReference=new WeakReference<String>(str);
    
            //虛引用 判斷對象是否已被回收 很少用得上
            PhantomReference phantomReference=new PhantomReference<String>(str)
    
            System.out.println("強引用"+str);
            System.out.println("軟引用"+softReference.get());
            System.out.println("弱引用"+weakReference.get());
    
        }

    當點擊運行時候,輸出的結果。

    強引用String
    軟引用String
    弱引用String

    那么如何手動去銷毀其對象實例呢?
    強引用可以手動將對象置為null。
    弱引用和軟引用可以手動調用clear()的方法,弱引用也可以調用gc進行回收,代碼如下。

     public  static  void main(String[] args){
    
            //強引用 內存無論怎么樣系統都不會釋放
            String str=new String("String");
    
            //軟引用 內存不足就釋放掉
            SoftReference<String> softReference=new SoftReference<String>(str);
    
            //弱引用 只要系統產生了GC(垃圾回收)它引用的對象就會被釋放掉
            WeakReference<String> weakReference=new WeakReference<String>(str);
    
            //虛引用 判斷對象是否已被回收
            PhantomReference phantomReference=new PhantomReference<String>(str)
    
            str=null;
            softReference.clear();
            System.out.println("強引用"+str);
            System.out.println("軟引用"+softReference.get());
            System.gc();
            //這種方式也是可以的
            weakReference.clear();
            System.out.println("弱引用"+weakReference.get());
    
        }

    運行輸出效果

    強引用null
    軟引用null
    弱引用null


    什么是內存泄漏?

    舉一個例子,如果一個Activity啟動的時候執行了長時間的耗時操作,當該耗時操作并未完全結束時,用戶點擊了back回退,此時的系統產生的Activity頁面消失,后臺耗時操作仍然運行并持有Activity的對象實例,這樣就會導致內存泄漏。也就是說,無用對象仍然被引用而導致內存泄漏。
    為了提高開發的穩定性,使用相關的工具進行查找內存泄漏的原因。


    LeakCannary

    這是第三方工具,可以方便得運用到項目中并快速的找到app是否存在內存泄漏的隱患。

    1.首先是添加依賴

    dependencies {

           debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
           releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
           testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
    

    }

    2.創建一個類繼承自Application,在onCreate()方法中添加如下代碼:

    public class ExampleApplication extends Application {
    
      @Override public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
          // This process is dedicated to LeakCanary for heap analysis.
          // You should not init your app in this process.
          return;
        }
        LeakCanary.install(this);
        // Normal app init code...
      }
    }

    這樣就能使用了。貼代碼。

    MainActivity類

    public class MainActivity extends Activity implements View.OnClickListener {
    
        private Button btn_press;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btn_press= (Button) findViewById(R.id.btn_press);
            btn_press.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
            MyThread myThread = new MyThread();
            myThread.start();
        }
    
        public class MyThread extends Thread{
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    MyApplication類

    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            if (LeakCanary.isInAnalyzerProcess(this)) {
                // This process is dedicated to LeakCanary for heap analysis.
                // You should not init your app in this process.
                return;
            }
            LeakCanary.install(this);
            // Normal app init code...
        }
    }
    

    添加一個按鈕

     <Button
            android:id="@+id/btn_press"
            android:text="Press"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    運行效果。當點擊按鈕是按back回退到主界面。過一會在導航欄的位置就會出現相應的泄漏信息。
    這里寫圖片描述


    常見的內存泄漏

    內部類隱式持有外部類的引用導致的內存泄漏。
    好比上面演示的例子。當Activity創建的實例被系統銷毀時,創建的MyThread類依舊持有activity的對象所以報錯。

    解決的辦法有三種,分別是在
    - 將MyThread類變成靜態類,
    - 在包下重新創建一個MyThread類。然后在MainActivity調用就行了。
    - 采用弱引用的方式

    這里演示一下弱引用的方式防止內存泄漏。貼上代碼。修改上面的代碼。

    public class MainActivity extends Activity implements View.OnClickListener {
    
        private Button btn_press;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btn_press= (Button) findViewById(R.id.btn_press);
            btn_press.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
            MyThread myThread = new MyThread(MainActivity.this);
            myThread.start();
        }
    
        public static class MyThread extends Thread{
    
            //創建一個弱引用對象
            private WeakReference<MainActivity> mReference=null;
            //在構造函數初始化弱引用的對象
            public MyThread(MainActivity activity){
                this.mReference=new WeakReference<MainActivity>(activity);
            }
            @Override
            public void run() {
                //取得弱引用的對象并和activity關聯
                MainActivity activity = mReference.get();
                for (int i = 0; i < 20; i++) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    這樣工具就不會提示出錯了。


    Handler內存泄漏

    Handler經常存在內存溢出的隱患,因為Handler發送消息過程中,可能存在長時間的耗時操作。為了避免這種情況,我們同樣適用弱引用來防止內存泄漏。
    代碼演示一下,

    public class MainActivity extends Activity implements View.OnClickListener {
    
        private Button btn_press;
        private Handler mhandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 1:
                        btn_press.setText("接收消息,修改文本");
                        break;
                }
    
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btn_press= (Button) findViewById(R.id.btn_press);
            btn_press.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
            StartHandler();
        }
    
        private void StartHandler() {
            Message message=Message.obtain();
            message.what=1;
            message.obj="發送延遲任務";
            mhandler.sendMessageDelayed(message,20000);
        }
    
    }

    這里寫圖片描述

    雖然按鈕的文本確實改變了,但是工具依舊會報錯,這是因為,Handler中的Looper類,最后需要從MessageQueue中取出消息,如果我們在發送消息的工程共需要20s的延遲時間,那么Looper這個類就一直處于等待的狀態所以導致了內存泄漏。
    同樣,解決的辦法是使用弱引用的方法。
    貼代碼

    public class MainActivity extends Activity implements View.OnClickListener {
    
        private Button btn_press;
    
        //將MainActivity 傳進去
        private MyHandler myHandler=new MyHandler(MainActivity.this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btn_press= (Button) findViewById(R.id.btn_press);
            btn_press.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
            StartHandler();
        }
    
        private void StartHandler() {
            Message message=Message.obtain();
            message.what=1;
            message.obj="發送延遲任務";
            mhandler.sendMessageDelayed(message,20000);
        }
    
        private static class MyHandler extends  Handler{
            private WeakReference<MainActivity> mReference;
            public MyHandler(MainActivity activity){
                this.mReference=new WeakReference<MainActivity>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                MainActivity mainActivity = mReference.get();
                if(mainActivity==null){
                    return ;
                }
                switch (msg.what){
                    case 1:
                        //通過軟引用中的對象可以拿到外部非靜態的Button對象
                        mainActivity.btn_press.setText("修改了按鈕文本");
                        break;
                }
    
            }
        }
    }

    這里寫圖片描述


    Fragment如何檢測內存泄漏?

    對于Fragment的內存泄漏,它有特殊的方法。
    需要在MyApplication創建一個RefWatcher觀察者對象。

    public class MyApplication extends Application {
    
        public static RefWatcher mRefWatcher;
    
        @Override public void onCreate() {
            super.onCreate();
            //...
            mRefWatcher = LeakCanary.install(this);
            // Normal app init code...
        }
    }   

    在Fragment的onDestroy()方法中添加

    MyApplication.mRefWatcher.watch(this);

    同理方法案例和上面相同。讀者自己摸索。

    版權聲明:本文為qq_29157591原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_29157591/article/details/76068832

    智能推薦

    安卓學習筆記——URI

    一、URI:統一資源定位符,Universal Resource Identifier的簡稱 每一個ContentProvider都擁有一個公共的URI,這個URI用于表示ContentProvider提供的數據 二、URI的組成: 例: 三、URI 常用方法: 1、static Uri parse(String UriString):將一個字符串轉換為Uri 例如: 2、 List getPat...

    內存優化之內存泄漏

    我們能通過這篇文章學到什么?我們這篇文章主要通過兩點來講: 探討關于內存泄露的原因; 1.1 什么是內存泄漏 1.2 導致內存泄露的根本原因 實戰找出內存泄漏并解決; 2.1 怎么定位內存泄漏 2.2 怎么解決內存泄漏 什么是內存泄漏 內存泄漏是指對象不再被應用程序使用,但是垃圾回收器卻不能移除它們,因為它們正在被引用。為了方便大家理解,我用一個例子幫助大家理解垃圾回收機制; 大家去過食堂吃飯吧,...

    SQLite數據庫存儲(一)【安卓學習筆記】

    對于MODE_PRIVATE,MODE_APPEND兩種模式,對寫文件的影響有什么不同? MODE_PRIVATE:該文件只能被當前程序讀寫,會把原來的內容覆蓋掉 MODE_APPEND:該文件的內容可追加,不會把原來的內容覆蓋掉,新寫的內容追加在文件后面 但是對于修改文件中的部分內容,應該怎么做呢?這就需要我們用到SQLite數據庫 主要內容: 創建和打開一個SQLite 數據庫 SQLite數...

    安卓專項測試之內存及PerfDog和SoloPi比較

    這次繼續安卓專項測試之內存 Android系統中每個APP占內存會有私有和公共的兩部分:ShareDirty、PrivateDirty。“PrivateDirty”內存是其最重要的部分,因為只被自己的進程使用。它只在內存中存儲,因此不能做分頁存儲到外存(Android不支持swap)。所有分配的Dalvik堆和本地堆都是“private dirty”...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    猜你喜歡

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    精品国产乱码久久久久久蜜桃不卡