2013-06-19 25 views
16

Trong một cuộc săn tìm rò rỉ bộ nhớ trong ứng dụng của tôi, tôi đã truy tìm một hành vi mà tôi không thể hiểu được. Tôi phân bổ một khối bộ nhớ lớn, nhưng nó không nhận được rác thu được kết quả trong một OOM, trừ khi tôi rõ ràng null tham chiếu trong onDestroy.Bộ nhớ lớn không phải rác thải được thu thập

Trong ví dụ này, tôi có hai hoạt động gần như giống hệt nhau chuyển đổi giữa nhau. Cả hai đều có một nút duy nhất. Khi nhấn nút MainActivity sẽ khởi động OOMActivity và OOMActivity trả về bằng cách gọi kết thúc(). Sau khi nhấn các nút một vài lần, Android sẽ ném ra một nhận thức.

Nếu tôi thêm onDestroy vào OOMActivity và rõ ràng null tham chiếu đến phần bộ nhớ, tôi có thể thấy trong nhật ký rằng bộ nhớ được giải phóng một cách chính xác.

Tại sao bộ nhớ không được tự động giải phóng mà không có giá trị rỗng?

MainActivity:

package com.example.oom; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 

public class MainActivity extends Activity implements OnClickListener { 

    private int buttonId; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     System.gc(); 
     Button OOMButton = new Button(this); 
     OOMButton.setText("OOM"); 
     buttonId = OOMButton.getId(); 

     setContentView(OOMButton); 
     OOMButton.setOnClickListener(this); 
    } 

    @Override 
    public void onClick(View v) { 
     if (v.getId() == buttonId) { 
      Intent leakIntent = new Intent(this, OOMActivity.class); 
      startActivity(leakIntent); 
     } 
    } 

} 

OOMActivity:

public class OOMActivity extends Activity implements OnClickListener { 

    private static final int WASTE_SIZE = 20000000; 
    private byte[] waste; 
    private int buttonId; 

    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Button BackButton = new Button(this); 
     BackButton.setText("Back"); 
     buttonId = BackButton.getId(); 

     setContentView(BackButton); 
     BackButton.setOnClickListener(this); 

     waste = new byte[WASTE_SIZE]; 

    } 

    public void onClick(View view) { 
     if (view.getId() == buttonId) { 
      finish(); 
     } 
    } 

} 
+0

ví dụ tuyệt vời, câu hỏi hay! – WarrenFaith

+0

Vui lòng đọc cuộc trò chuyện này. Chúng tôi đã [thảo luận ở đây] (http://chat.stackoverflow.com/transcript/message/10084186#10084186) – Reno

+2

Có thể bạn đang thử nghiệm điều này trên thiết bị trước tổ ong. Bộ thu gom rác trên hậu tổ ong sẽ đủ mạnh để giải phóng đối tượng "rác". Thử nghiệm trên 4.1.2, 4.2.2, 2.3.5, 2.3.7. Gọi System.gc() trước khi "waste = new byte [WASTE_SIZE]" tránh sự cố trên thiết bị trước tổ ong. – pellucide

Trả lời

1

Hoạt động phá hoại nào không bao hàm sự phá hủy lớp, chỉ vì bạn không nhìn thấy các lớp không có nghĩa là hệ điều hành (Android trong trường hợp này) mất tất cả các tham chiếu đến nó và kết thúc nó. Đó là lý do tại sao ngay cả trong các tài liệu họ chỉ định để làm sạch ra bất kỳ xử lý và các đối tượng bạn không còn cần phải ngăn chặn rò rỉ bộ nhớ. Chúc mừng.

1

Một vài điều:

1) Bạn không thể đánh giá xem Hoạt động của bạn có bị rò rỉ hay không bằng cách xem nhật ký GC; mỗi lần thực hiện JVM là miễn phí để chọn khi nào thu thập rác đối tượng, ngay cả khi không có gì là tham chiếu đến chúng. Lưu ý rằng nó là cần thiết để thu thập rác trước khi nó ném một lỗi OOM ... nhưng nếu nó có đủ bộ nhớ có sẵn, nó có thể chọn để giữ 10 hoạt động của bạn trong bộ nhớ và sau đó thu thập tất cả cùng một lúc.

2) Có thể có cấu trúc Android bên trong giữ tham chiếu đến Hoạt động của bạn lâu hơn vòng đời của Hoạt động ... và nhà phát triển không kiểm soát được điều đó. Vì lý do đó, bạn nên tham khảo Hoạt động không tham chiếu bất kỳ lượng lớn dữ liệu nào (hoặc nếu có, nó sẽ giải phóng rõ ràng các tham chiếu đó trong onDestroy (hoặc thậm chí ở chế độ onPause nếu bạn muốn tích cực hơn)

3) Một số JVMs tối ưu hóa khi chạy, như vậy một đoạn bộ nhớ không bao giờ được ghi vào hoặc truy cập không bao giờ thực sự được cấp phát trong bộ nhớ vật lý. Có thể vì lý do đó, kiểm tra của bạn không hợp lệ trên các phiên bản Android mới hơn. Để giải quyết vấn đề này, bạn có thể thêm một vòng lặp đặt một số giá trị trong mảng thành các giá trị ngẫu nhiên, và sau đó lặp lại một nơi khác trong mã đọc chúng; theo cách này, JVM buộc phải cấp phát bộ nhớ.

Các vấn đề liên quan