Android之內存泄露、內存溢出、內存抖動分析


 

內存

 
JAVA是在JVM所虛擬出的內存環境中運行的,內存分為三個區:堆、棧和方法區。
棧(stack):是簡單的數據結構,程序運行時系統自動分配,使用完畢后自動釋放。優點:速度快。
堆(heap):用於存放由new創建的對象和數組。在堆中分配的內存,一方面由java虛擬機自動垃圾回收器來管理,另一方面還需要程序員提供修養,防止內存泄露問題。
方法區(method):又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變量。
 

Java GC

 

GC可以自動清理堆中不在使用(不在有對象持有該對象的引用)的對象。

在JAVA中對象如果再沒有引用指向該對象,那么該對象就無從處理或調用該對象,這樣的對象稱為不可到達(unreachable)。垃圾回收用於釋放不可到達的對象所占據的內存。

對android來說,內存使用尤為吃緊,最開始的app進程最大分配才8M的內存,漸漸增加到16M、32M、64M,但是和服務端相比還是很渺小的。如果對象回收不及時,很容易出現OOM錯誤。

 

內存泄露

 
什么是內存泄露?程序通過new分配內存,在使用完畢后沒有釋放,造成內存占用。這塊內存不受GC控制,無法通過GC回收。
主要表現在:當一個對象已經不再使用,本該被回收的,但是另外一個正在使用的對象持有它的引用從而就導致對象不能被回收。這種對象存在堆內存中,就產生了內存泄漏。
 

危害?內存泄漏對於app沒有直接的危害,即使app有發生內存泄漏的情況,也不一定會引起app崩潰,但是會增加app內存的占用。內存得不到釋放,慢慢的會造成app內存溢出。解決內存泄漏目的就是防止app發生內存溢出。

 
內存泄露主要表現的當Activity在finish的時候,由於對象持有對Activity的引用,造成Activity沒有被及時回收。總結了下大致有5種情況造成內存泄露,(1)static變量、匿名類的使用 (2)線程執行處理(3)各種監聽回調處置(4)Bitmap等回收處置(5)集合類只有增操作卻沒有減操作。
 
常見情況
1)外部類持有Activity的靜態引用
[java]  view plain  copy
 
  1. public class MainActivity extends AppCompatActivity {  
  2.     static Activity activity;  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.         CommUtil commUtil = CommUtil.getInstance(this);  
  9.     }  
[java]  view plain  copy
 
  1. public class CommUtils {  
  2.     private static CommUtils instance;  
  3.     private Context context;  
  4.   
  5.     private CommUtils(Context context) {  
  6.         this.context = context;  
  7.     }  
  8.   
  9.     public static CommUtils getInstance(Context context) {  
  10.         if (instance == null) {  
  11.             instance = new CommUtils(context);  
  12.         }  
  13.         return instance;  
  14.     }  
  15. }  
2)異步執行耗時任務期間時,Thread、AsyncTask、TimeTask持有的Activty進行finish時,Activity實例不會被回收。
[java]  view plain  copy
 
  1. protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3.         setContentView(R.layout.activity_main);  
  4.         new AsyncTask<String, Void, String>() {  
  5.             @Override  
  6.             protected String doInBackground(String... params) {  
  7.                 for (int i = 0; i < 15; i++) {  
  8.                     try {  
  9.                         Log.e("MainActivity2", "dddd" + i + MainActivity2.this.getLocalClassName());  
  10.                         Thread.sleep(1000);  
  11.                     } catch (InterruptedException e) {  
  12.                         e.printStackTrace();  
  13.                     }  
  14.                 }  
  15.                 return null;  
  16.             }  
  17.   
  18.             @Override  
  19.             protected void onPostExecute(String s) {  
  20.                 super.onPostExecute(s);  
  21.             }  
  22.         }.execute();  
  23.     }  
3)Handler內部類造成內存泄露。
Handler為非靜態內部類時會隱式持有當前activity引用。當Activity被 finish()時,若Handler有未處理完或延遲的消息(主要是Handler牽扯到線程問題),會造成activity不能被回收。
[java]  view plain  copy
 
  1. MyHandler myHandler = new MyHandler();  
  2.   
  3.    @Override  
  4.    protected void onCreate(@Nullable Bundle savedInstanceState) {  
  5.        super.onCreate(savedInstanceState);  
  6.        myHandler.postDelayed(new Runnable() {  
  7.            @Override  
  8.            public void run() {  
  9.   
  10.            }  
  11.        }, 50 * 1000);  
  12.    }  
  13.   
  14.    class MyHandler extends Handler {  
  15.   
  16.        @Override  
  17.        public void handleMessage(Message msg) {  
  18.            super.handleMessage(msg);  
  19.        }  
  20.    }  

解決辦法:在Activity生命周期結束前,確保Handler移除消息(mMyHanlder.removeCallbacksAndMessages(null);)或者使用靜態Handler內部類。
如:使用了弱引用替代強引用.
[java]  view plain  copy
 
  1. static MyHandler myHandler;  
  2.     @Override  
  3.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         myHandler = new MyHandler(this);  
  6.     }  
  7.     static class MyHandler extends Handler {  
  8.         WeakReference<Activity> mActivityReference;  
  9.   
  10.         MyHandler(Activity activity) {  
  11.             mActivityReference = new WeakReference<Activity>(activity);  
  12.         }  
  13.   
  14.         @Override  
  15.         public void handleMessage(Message msg) {  
  16.             final Activity activity = mActivityReference.get();  
  17.             if (activity != null) {  
  18.                 //....  
  19.             }  
  20.         }  
  21.     }  
建議熟悉下:強引用(StrongReference)、軟引用(SoftReference)、弱引用(WeakReference)、虛引用(PhantomReference)
 
4)匿名內部類的使用。
[java]  view plain  copy
 
  1. public class DemoActivity extends AppCompatActivity {  
  2.       
  3.     Runnable runnable = new Runnable() {  
  4.         @Override  
  5.         public void run() {  
  6.   
  7.         }  
  8.     };  
runnable默認會持有DemoActivity的引用。若Activity被finish的時候,如線程在使用runnable,則會造成內存泄露。
 
5)構造Adapter時沒有使用緩存的 convertView
 
[java]  view plain  copy
 
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.         View view = null;  
  3.         if (convertView == null)  
  4.             convertView = View.inflate(this, R.layout.item_layout, false);  
  5.         view = convertView;  
  6.         return view;  
  7.     }  
6) 當使用了BraodcastReceiver、Cursor、Bitmap等資源時,若沒有及時釋放,則會引起內存泄漏。
7)集合類的不當使用。
 

更多內存泄露可以通過檢測工具發現。檢測工具主要有MAT、Memory Monitor 、Allocation Tracker 、Heap Viewer、LeakCanary

 

內存溢出

什么是內存溢出?out of memory。 程序向系統申請的內存空間超出了系統能給的。內存泄露很容易引起OOM。
 

內存抖動

內存抖動是指在短時間內有大量的對象被創建或者被回收的現象,主要是循環中大量創建、回收對象。這種情況應當盡量避免。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM