理解Java中的內存泄漏,咱們首先要清楚Java中的內存區域分配問題和內存回收的問題本文將分爲三大部分介紹這些內容。java
Java中的內存區域主要分爲線程共享的和線程私有的兩大區域: android
介紹了Java的內存分配問題,經過一段代碼來進行一下總結算法
public static void main(String[] args) {
Animal animal = new Animal();
}
publci class Animal{
public static String address = "the earth"
public Animal(){
}
}
複製代碼
在main方法中,咱們new Animal(),首先,檢查內存中是否加載了Animal.class,若是沒有加載,則會先將Animal.class加載到方法區中,同時在方法區開闢一塊內存區域,用於存儲static 的"the earth"(這裏更能清晰的理解static這個關鍵字,就是static修飾的變量是屬於類的,而不是屬於類的某一個對象的),隨後在Java堆中開闢一塊內存區域,用於存儲new Animal()這個對象,在虛擬機棧中的animal會指向這個對象的地址。數組
Java內存回收主要是指的是Java堆上的已經分配給對象的內存回收,判斷Java堆上的內存是否被回收,主要是經過可達性分析算法:從一系列能夠做爲 GC Roots 的對象開始向下搜索,搜索走過的路徑稱爲引用鏈,當GC回收時,一個對象沒有經過引用鏈與任何GC Roots對象鏈接,則這個對象就能夠被回收了。可做爲GC Roots對象的有如下幾種:bash
須要注意的是GC Roots並非一直能夠做爲GC Roots的,eg:網絡
public void testGc(){
Person person = new Person();
}
複製代碼
根據GC Roots的定義,new Person()這個對象被person所引用,person在虛擬機棧中,因此new Person()這個對象是做爲了GC Roots的,可是當這個testGc()方法執行完成,person釋放內存,new Person()這個對象就沒有其餘的引用,就再也不是GC Roots。ide
內存泄漏須要和內存溢出區分開來,內存泄漏是指:部份內存咱們再也不須要了,可是虛擬機不能回收,泄漏過多就會形成內存溢出。就是部分對象咱們已經用不上了,可是這些對象和GC Roots存在必定程度上的引用關係,致使不能被垃圾回收機制回收。ui
public class InnerStactivity extends AppCompatActivity {
private static Object ininerClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inner_stactivity);
}
class InnerClass{
}
public void startInnerClass(){
ininerClass = new InnerClass();
}
/**
* 建立了InnerClass的靜態實例引用
* @param view
*/
public void createInner(View view) {
startInnerClass();
}
}
複製代碼
當static Object ininerClass指向了newInnerClass()這個對象時,這個對象就能夠做爲了GC Roots,同時非靜態的內部類會持有外部類的引用,InnerStactivity就會在GC Roots的引用鏈上,當咱們須要離開這個InnerStactivity,而且再也不須要這個InnerStactivity時,這個InnerStactivity並不能被回收。this
匿名內部類的靜態實例spa
Handler內存泄漏
public class HandlerActivity extends AppCompatActivity {
Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mHandler= new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
show();
}
};
}
public void sendMessage(View view) {
mHandler.sendEmptyMessageDelayed(1,1000*30);
finish();
}
public void show(){
}
}
複製代碼
能夠看到咱們這麼寫Android studio已經提示了警告,提示咱們應該用static修飾handler對象,不然會形成內存的泄漏,這是不容易犯的錯誤。
Handler這麼寫之因此會出現內存泄漏是由於:Message會持有一個對Handler的引用,當這個Handler是非靜態內部類的時候,又會持有一個對外部類的引用(好比Activity)。若是發送一條延時的Message,因爲這個Message會長期存在於隊列中,就會致使Handler長期持有對Activity的引用,從而引發視圖和資源泄漏。當你發送一條延時的Mesaage,而且把這個Message保存在某些地方(例如靜態結構中)備用,內存泄漏會變得更加嚴重。
咱們如今都經過static修飾 Handler類,並經過弱引用來和當前界面的Activity交互,並在onDestory()中去除Handler的全部的消息來規避可能出現的內存泄漏。
public class HandlerImproveActivity extends AppCompatActivity {
Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mHandler= new ImproveHandler(HandlerImproveActivity.this);
}
public void sendMessage(View view) {
mHandler.sendEmptyMessageDelayed(1,1000*30);
finish();
}
private static class ImproveHandler extends Handler{
private WeakReference<HandlerImproveActivity> mActivity;
public ImproveHandler(HandlerImproveActivity improveActivity) {
this.mActivity = new WeakReference<HandlerImproveActivity>(improveActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mActivity != null && mActivity.get() != null) {
mActivity.get().show();
}
}
}
public void show(){
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mHandler != null){
mHandler.removeCallbacksAndMessages(null);
}
}
}
複製代碼
AlertDialog alertDialog= new AlertDialog.Builder(this).create();
Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT).show();
複製代碼
Android Developer中關於如何管理內存的連接 developer.android.com/topic/perfo…