How References work in Java and Android
Bài đăng này đã không được cập nhật trong 4 năm
Trong Java, GC (Garbage Collector) là chương trình chạy nền, làm nhiệm vụ theo dõi các đối tượng trong bộ nhớ, và tiến hành thu hồi bộ nhớ (quá trình Garbage Collection) của các đối tượng khi chúng không còn được tham chiếu đến. Developer cần nắm biết khi nào đối tượng được xem là thỏa mãn điều kiện để  GC thu hồi, bởi chúng ta không hề mong muốn các đối tượng không cần thiết được lưu giữ trong bộ nhớ. Trong bài viết này, chúng ta sẽ cùng tìm hiểu thêm về Reference (tạm dịch là tham chiếu) trong Java nói chung và Android nói riêng.
Khái niệm
A reference is the direction of an object that is annotated, so you can access it.
tạm dịch là:
Một tham chiếu chỉ đến một đối tượng được khai báo, nhờ đó ta có thể truy cập được nó.
Phân loại
Trong Java, Reference được chia làm 4 loại là:
- Strong reference (tạm dịch: tham chiếu mạnh)
 - Weak reference (tạm dịch: tham chiếu yếu)
 - Soft reference (tạm dịch: tham chiếu mềm)
 - Phantom reference (tạm dịch: tham chiếu ma)
 
Chúng ta sẽ lần lượt đi qua từng loại reference này.
Strong reference
Là loại tham chiếu mặc định, và thường gặp trong Java. Khi tạo một đối tượng thì mặc định một Strong reference được tạo ra bên trong đối tượng.
MyObject obj = new MyObject();
Một đối tượng sẽ không bị GC thu hồi nếu như có bất cứ một chuỗi Strong reference nào liên kết với nó.
public class StrongRefDemo {
    static class MyObject {
        @ Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("I'm collected!"); // will be printed if  MyObject is collected by GC
        }
    }
    static MyObject obj;
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Start");
        obj = new MyObject(); // New strong reference is created.
        obj = null; // To remove strong reference from obj
        System.gc(); // Force to call Garbage collector
        Thread.sleep(5000); // Wait GC finish its job
        System.out.println("End");
    }
}
Trong ví dụ trên, đối tượng obj mất đi Strong reference sau khi gán =null nên thỏa mãn điều kiện để GC thu hồi bộ nhớ (dòng I'm collected! được in ra trong log).
Note:
Trong
java, hàmfinalize()đượcGCgọi trên đối tượng khiGCnhận thấy rằng không còn một tham chiếu nào tới đối tượng đó.

Xét ví dụ tiếp sau đây:
public static void main(String args[]) throws InterruptedException {
        System.out.println("Start");
        obj = new MyObject();
        List<MyObject> list = new ArrayList<MyObject>();
        list.add(obj);
        obj = null;
        System.gc();
        Thread.sleep(5000);
        System.out.println("End");
    }
Trong trường hợp này, đối tượng obj tuy gán =null. Nhưng trước đó, nó đã được thêm vào trong list nên sau khi gán null, nó vẫn là 1 Strongly reachable vì tồn tại 1 đối tượng có tham chiếu đến nó. Do vậy, đối tượng obj sẽ không bị thu hồi.
Để  GC có thể thu hồi obj ta cần hủy các tham chiếu đến nó bằng cách gán list = null hoặc list.clear().
Lấy 1 ví dụ thường thấy trong android:
public class MainActivity extends Activity {
    @ Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        new MyAsyncTask().execute();
    }
    private class MyAsyncTask extends AsyncTask {
        @ Override
        protected Object doInBackground(Object[] params) {
            return doSomeStuff();
        }
        private Object doSomeStuff() {
            //do something to get result
            return new MyObject();
        }
    }
}
Trong đoạn code này đối tượng MyAsyncTask có Strong reference với MainActivity, theo đó có 1 vấn đề nảy sinh khi MainActivity bị hủy (onDestroy) và cần được thu hồi bộ nhớ, lúc này giả sử  MyAsyncTask vẫn đang thực thi và giữ tham chiếu đến MainActivity, thì điều này làm cho GC không thể thu hồi bộ nhớ của activity, dẫn đến tình trạng leak memory.
WeakReference
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed.
tạm dịch (+ thêm bớt 
 )
Tham chiếu yếu là tham chiếu mà không đủ mạnh để giữ đối tượng đó trong bộ nhớ khi bị yêu cầu thu hồi (từ GC)

JVM sẽ bỏ qua với Weak reference, các đối tượng weakly reachable  object có thể  truy cập qua method get() nếu như nó vẫn còn trong bộ nhớ.
public static void main(String args[]) {
    obj = new MyObject();
    WeakReference<MyObject> weakObj = new WeakReference<>(obj)
    obj = null; // obj is now eligible for garbage collection.
    // System.gc(); // if gc run here, obj is completely collected
    obj = weakObj.get(); // try to retrieve back obj
}
Ở ví dụ này, obj có thể được thu hồi sau khi gán null nếu nó vẫn tồn tại trong bộ nhớ (GC chưa chạy).
Trong android thì sao, xét ví dụ sau đây:
public class MainActivity extends Activity {
    @ Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new MyAsyncTask(this).execute();
    }
    private static class MyAsyncTask extends AsyncTask {
        private WeakReference<MainActivity> mainActivity;
        public MyAsyncTask(MainActivity mainActivity) {
            this.mainActivity = new WeakReference<>(mainActivity);
        }
        @ Override
        protected Object doInBackground(Object[] params) {
            return doSomeStuff();
        }
        private Object doSomeStuff() {
            //do something to get result
            return new Object();
        }
        @ Override
        protected void onPostExecute(Object object) {
            super.onPostExecute(object);
            if (mainActivity.get() != null){
                //adapt contents
            }
        }
    }
}
Khác với trong ví dụ với Strong reference, MainActivity bị leak memory. Trong ví dụ này MyAsyntask tham chiếu với MainActivity thông qua WeakReference.
private WeakReference<MainActivity> mainActivity;
...
this.mainActivity = new WeakReference<>(mainActivity);
Do vậy, khi MainActivity bị hủy, WeakReference giúp nó được GC thu hồi, và không còn tình trạng memory leak.
SoftReference
SoftReference giống với WeakReference ở điểm nó có thể được thu hồi khi GC cần, tuy nhiên điểm khác biệt là trong khi WeakReference được thu hồi ngay lập tức, còn SoftReference lại phụ thuộc vào GC. Tất cả các SoftReference được cho vào các reference Queue và xóa khi cần.
GC đảm bảo rằng tất cả các SoftReference tới các đối tượng softly reachable sẽ được thu hồi trước khi hệ thống văng lỗi OutOfMemory, ưu tiên giữ lại những tham chiếu là recently-created hoặc recently-used.
Trong android, không khuyên khích việc sử dụng SoftReference cho việc tạo cache. Lý do là hệ thống sẽ không có đủ thông tin để xóa hoặc giữ tham chiếu nào và có thể dẫn đến tình trạng heap tăng cao. Việc thiếu thông tin này làm giảm công dụng của SoftReference, GC có thể thu hồi sớm những tham chiếu còn dùng, hoặc hao phí bộ nhớ khi giữ những tham chiếu không cần thiết. Vậy nên sử dụng android.util.LruCache thay cho SoftReference khi tạo cache.
PhantomReference
An object is phantom reachable if it is neither strongly nor softly nor weakly reachable and has been finalized and there is a path from the roots to it that contains at least one phantom reference.
tạm dịch (khá khó hiểu)
Một đối tượng là
phantom reachablenếu như nó không phải làstrongly, weakly hay softly reachable, đã ở trạng tháifinalized(tức lúcGCxác định là không còn tham chiếu nào tới đối tượng này) và có 1 đường đi từrootstới đối tượng đó chứa ít nhất 1phantom reference
Khác với các tham chiếu khác, PhantomReference không tự động bị thu hồi bởi GC mà nó sẽ được cho vào ReferenceQueue, một đối tượng phantom reachable sẽ tồn tại đến khi tất cả các tham chiếu bị hủy hoặc bản thân nó trở thành unreachable. Khác với WeakReference and SoftReference, hàm get() trong PhantomReference luôn trả về  null
(Dịch sang tiếng Việt khá khó hiểu phần này 
 , mọi người có thể tham khảo chi tiết hơn về  PhantomReference tại đây )
Kết luận
Qua bài viết hi vọng mọi người có thêm 1 ít thông tin hữu ích về những loại reference trong java, và biết được khi nào đối tượng sẽ được GC thu hồi bộ nhớ.
Với android, tránh sử dụng non-static inner Class , mà nên sử dụng static inner Class kết hợp WeakReference để  tránh memory leak. Tránh sử dụng SoftReference cho việc tạo cache trong android.
Tham khảo:
https://dzone.com/articles/finalization-and-phantom https://developer.android.com/reference/java/lang/ref/PhantomReference.html https://developer.android.com/reference/java/lang/ref/WeakReference.html https://developer.android.com/reference/java/lang/ref/SoftReference.html
All rights reserved