-1

Quản lý bộ nhớ của ứng dụng trong Android

Performance, một thứ vô cùng quan trọng đối với một ứng dụng trong Android, ảnh hưởng trực tiếp đến cảm quan người dùng, đặc biệt với các ứng dụng quy mô lớn. Hôm nay chúng ta sẽ đến với bài nhập môn về performance, đó là quản lý bộ nhớ của ứng dụng trong Android.

Random-access memory (RAM) là một nguồn tài nguyên quý giá trong bất kỳ môi trường phát triển phần mềm nào, và nó còn có giá trị hơn trên một hệ điều hành di động, nơi bộ nhớ vật lý thường được hạn chế. Mặc dù máy ảo Dalvik Android thông thường sẽ thực hiện việc thu gom rác thải, nhưng điều này cũng không cho phép bạn bỏ qua khi nào và nơi bạn đã cấp phát hay giải phóng bộ nhớ.

Để cho các bộ thu rác có thể lấy lại bộ nhớ từ ứng dụng, bạn cần tránh vấn đề memory leak và giải phóng bất kỳ một Refernce object nào tại thời điểm thích hợp. Với phần lớn ứng dụng, bộ gom rác Dalvik sẽ sử lý phần còn lại: hệ thống đòi cấp phát bộ nhớ khi object tương ứng ròi khỏi phạm vi của active thread của ứng dụng.

  1. Cách để quản lý bộ nhớ ứng dụng Android

    Android không cung cấp không gian trao đổi về bộ nhớ, nhưng nó sử dụng paging và memory-mapping (mmapping) để quản lý bộ nhớ.Nghĩa là bất kỳ bộ nhớ nào bạn sửa đổi cho dù bằng cách cấp phát các đối tượng mới hoặc động đến các trang mmapped -vẫn thường trú trong bộ nhớ RAM và không thể được paged ra. Vì vậy, cách duy nhất để hoàn toàn giải phóng bộ nhớ từ ứng dụng của bạn là để phát hành đối tượng quan hệ bạn có thể nắm giữ, làm cho bộ nhớ có thể được giải phóng. Đó là với một ngoại lệ: bất kỳ tập tin mmapped trong mà không sửa đổi, chẳng hạn như mã số, có thể được paged ra RAM nếu hệ thống muốn sử dụng bộ nhớ ở nơi khác.

  2. Chia sẻ bộ nhớ

    Để làm phù hợp tất cả mọi thứ nó cần trong bộ nhớ RAM, Android sẽ cố gắng để chia sẻ các trang bộ nhớ RAM trên quy trình. Nó có thể làm như vậy trong các cách sau:

    • Mỗi một tiến trình ứng dụng được chia ra từ một tiến trình có sẵn có tên Zygote. Tiến trình Zygote bắt đầu khi khi hệ thống khởi động và load các mã framework và resource chung (giống như activity theme). Để bắt đầu một ứng dụng tiến trình mới, hệ thống chia tiến trình Zygote ra, sau đó load và chạy code của ứng dụng trong một tiến trình mới. Điều này cho phép hầu hết các trang bộ nhớ RAM cấp phát cho code framework và resource có thể chia sẻ cho tất cả các tiến trình trong app.

    • Hầu hết dữ liệu tĩnh được ánh xạ vào một tiến trình. Điều này không chỉ cho phép cùng một dữ liệu được chia sẻ giữa các tiến trình mà còn cho phép nó được paged ra khi cần thiết.

    • Ở nhiều nơi, Android chia sẻ RAM động qua quá trình sử dụng được phân bổ một cách rõ ràng các vùng bộ nhớ chia sẻ (hoặc với ashmem hoặc gralloc). Ví dụ, bề mặt cửa sổ sử dụng bộ nhớ giữa các ứng dụng và màn hình compositor chia sẻ, và bộ đệm con trỏ sử dụng bộ nhớ giữa các nhà cung cấp nội dung và khách hàng chia sẻ.

  3. Cấp phat và đòi bộ nhớ Sau đây là một điều về cách Android cấp phát và thu hồi bộ nhớ trong app:

    • Dalvik heap cho mỗi quá trình được hạn chế đến một phạm vi bộ nhớ ảo duy nhất. Điều này xác định kích thước heap hợp lý, có thể phát triển như nó cần phải (nhưng chỉ đến một giới hạn mà hệ thống định nghĩa cho mỗi ứng dụng).

    • Kích thước hợp lý của heap không giống như số lượng bộ nhớ vật lý được sử dụng bởi nó. Khi kiểm tra heap của ứng dụng, Android tính một giá trị gọi là tỉ lệ Set Kích thước (PSS), chiếm cả các trang dirty và clean được chia sẻ với các quá trình khác, nhưng chỉ trong tổng số đó là tỷ lệ thuận với số ứng dụng được chia sẻ RAM. (PSS) tổng số này là những gì hệ thống coi là bộ nhớ vật lý của bạn.

    • Các heap Dalvik không thu gọn kích thước vật lý của heap, có nghĩa rằng Android không chống phân mảnh heap để dồn lại không gian. Android chỉ có thể thu nhỏ kích thước heap hợp lý khi có không gian chưa sử dụng vào cuối heap. Nhưng điều này không có nghĩa là bộ nhớ vật lý được sử dụng bởi các heap không thể co lại. Sau khi thu gom rác thải, Dalvik dẫn heap và tìm các trang không sử dụng, sau đó trả lại những trang này về kernel sử dụng madvise. Cải tạo bộ nhớ từ phân bổ nhỏ có thể có hiệu quả vì các trang được sử dụng cho một phân bổ nhỏ vẫn có thể được chia sẻ với những trang khác mà chưa được giải phóng.

  4. Hạn chế bộ nhớ ứng dụng

    Để duy trì một môi trường chức năng mutil-task, Android đặt một giới hạn cứng về kích thước heap cho mỗi ứng dụng. Giới hạn kích thước heap chính xác thay đổi giữa các thiết bị dựa trên tổng RAM mà thiết bị có sẵn. Nếu ứng dụng của bạn đã đạt đến giới hạn sức chứa của nó và cố gắng để cấp phát bộ nhớ hơn, nó sẽ nhận được một OutOfMemoryError.

    Trong một số trường hợp, bạn có thể muốn truy vấn hệ thống để xác định chính xác bao nhiêu không gian heap bạn đã có sẵn trên thiết bị hiện tại, để xác định có bao nhiêu dữ liệu an toàn để giữ trong bộ nhớ cache. Bạn có thể truy vấn hệ thống cho con số này bằng cách gọi getMemoryClass (). Nó trả về một số nguyên cho biết số lượng của MB có sẵn cho heap của ứng dụng của bạn.

  5. Chuyển các ứng dụng

    Thay vì sử dụng không gian trao đổi khi người dùng chuyển đổi giữa các ứng dụng, Android giữ các quy trình mà không được lưu trữ tại một nền trước ( "người dùng có thể nhìn thấy") thành phần ứng dụng trong một cache LRU (least-recently used). Ví dụ, khi người dùng đầu tiên ra mắt một ứng dụng, một tiến trình được tạo ra cho nó, nhưng khi người dùng rời khỏi ứng dụng, quá trình này không thoát. Hệ thống này giúp quá trình được lưu trữ, vì vậy nếu người dùng sau đó trở lại ứng dụng, tiến trình này được sử dụng lại cho các ứng dụng chuyển đổi nhanh hơn.

    Nếu ứng dụng của bạn có một tiến trình trình lưu trữ và nó vẫn nhớ rằng hiện tại nó không cần, kể cả người dùng không sử dụng nó, làm hạn chế hiệu suất tổng thể của hệ thống. Vì vậy, khi hệ thống chạy chậm trên bộ nhớ, nó có thể kill các quy trình trong bộ nhớ cache LRU bắt đầu với quá trình ít được sử dụng nhất, nhưng cũng đưa ra một số xem xét đối với các quy trình có nhiều bộ nhớ chuyên sâu.

  6. Cach nên làm để quản lý bộ nhớ

    Bạn nên chú ý hạn chế bộ nhớ RAM trong suốt quá trình phát triển, kể cả trong khi thiết kế ứng dụng (trước khi bạn bắt đầu phát triển). Có rất nhiều cách để bạn có thể thiết kế và viết mã dẫn kết quả cho hiệu quả hơn.

    Bạn nên áp dụng các kỹ thuật sau đây khi thiết kế và triển khai các ứng dụng của bạn để làm cho nó bộ nhớ hiệu quả hơn:

    6.1 Sử dụng dịch vụ một cách tiết kiệm

    Nếu ứng dụng của bạn cần một dịch vụ để thực hiện công việc trong nền, không giữ cho nó chạy, trừ khi nó chủ động thực hiện một công việc. Cũng phải cẩn thận để không bao giờ bị rò rỉ dịch vụ của bạn bằng cách không để ngăn chặn nó khi công việc của mình được thực hiện.

    Khi bạn bắt đầu một dịch vụ, hệ thống thích để luôn luôn giữ tiến trình cho dịch vụ đang chạy. Điều này làm cho tiến trình rất tốn kém vì bộ nhớ RAM được sử dụng bởi các dịch vụ không thể được sử dụng bởi bất cứ điều gì khác hoặc paged ra. Điều này làm giảm số lượng của các tiến trình lưu trữ hệ thống có thể giữ trong bộ nhớ cache LRU, làm cho chuyển đổi ứng dụng kém hiệu quả. Nó thậm chí có thể dẫn đến hư hại trong hệ thống khi bộ nhớ bị chật và hệ thống không thể duy trì đủ các quy trình để lưu trữ tất cả các dịch vụ đang chạy.

    Cách tốt nhất để hạn chế tuổi thọ của các dịch vụ của bạn là sử dụng một IntentService được kết thúc bởi bản thân ngay khi nó được thực hiện xử lý các intent mà bắt đầu nó.

    Chuyển một dịch vụ chạy khi nó không cần thiết là một trong những sai lầm tồi tệ nhất trong quản lý bộ nhớ một ứng dụng Android có thể thực hiện. Vì vậy, không được tham lam bằng cách giữ một dịch vụ cho các ứng dụng của bạn đang chạy. Không chỉ nó sẽ làm tăng nguy cơ ứng dụng của bạn hoạt động không tốt do RAM hạn chế, nhưng người dùng sẽ khám phá các ứng dụng misbehaving đó và gỡ bỏ chúng.

    6.2 Giải phóng bộ nhớ khi giao diện người dùng bị ẩn

    Khi người dùng điều hướng tới một ứng dụng khác và giao diện người dùng của bạn không còn nhìn thấy được, bạn nên giải phóng bất kỳ resource nào được sử dụng chỉ bởi giao diện của bạn. Giải phóng UI resource vào thời điểm này có thể làm tăng đáng kể khả năng của hệ thống cho quá trình lưu trữ, trong đó có một tác động trực tiếp đến chất lượng của trải nghiệm người dùng.

    Để được thông báo khi người dùng thoát khỏi UI của bạn, thực hiện hàm onTrimMemory () gọi lại trong các Activity. Bạn nên sử dụng phương pháp này để lắng nghe cho mức TRIM_MEMORY_UI_HIDDEN, mà chỉ ra UI của bạn hiện đang ẩn trên view và bạn nên giải phóng các resource mà chỉ có giao diện người dùng của bạn sử dụng.

    Chú ý rằng ứng dụng của bạn nhận được các callback onTrimMemory () với TRIM_MEMORY_UI_HIDDEN chỉ khi tất cả các thành phần giao diện người dùng của quá trình ứng dụng của bạn trở nên ẩn từ user. Đây là khác biệt từ onStop (), được gọi khi một Activity ẩn, mà xảy ra ngay cả khi người dùng di chuyển đến một Activity khác trong ứng dụng. Vì vậy, mặc dù nên thực hiện onStop () để giải phóng các resource như một kết nối mạng hoặc unregister thu phát sóng, bạn thường không nên release UI resource cho đến khi bạn nhận được onTrimMemory (TRIM_MEMORY_UI_HIDDEN). Điều này đảm bảo rằng nếu người dùng điều hướng trở lại từ một activity khác trong ứng dụng, UI resource vẫn có sẵn để tiếp tục hoạt động một cách nhanh chóng.

    6.3 Tránh lãng phí bộ nhớ với bitmap

    Khi load một bitmap, hãy giữ nó trong bộ nhớ RAM chỉ ở độ phân giải cần cho màn hình hiện tại, scale xuống nếu bitmap gốc có độ phân giải cao hơn.

    Lưu ý: Trên 2.3.x Android (cấp API 10) trở xuống, các đối tượng bitmap luôn xuất hiện cùng kích thước trong heap ứng dụng không phụ thuộc vào độ phân giải hình ảnh (các dữ liệu điểm ảnh thực tế được lưu giữ riêng trong bộ nhớ cục bộ). Điều này khiến khó khăn hơn để gỡ lỗi cấp phát bộ nhớ bitmap bởi vì hầu hết các công cụ phân tích heap không thấy việc phân bổ nguồn gốc. Tuy nhiên, bắt đầu từ Android 3.0 (API cấp độ 11), các dữ liệu pixel bitmap được phân bổ trong heap Dalvik của ứng dụng, cải thiện thu gom rác thải và debuggability. Vì vậy, nếu ứng dụng của bạn sử dụng bitmap và bạn đang gặp rắc rối phát hiện ra lý do tại sao ứng dụng đang sử dụng một số bộ nhớ trên thiết bị cũ, chuyển sang một thiết bị chạy Android 3.0 hoặc cao hơn để gỡ lỗi đó.

Tổng kết: chúng ta sẽ đến với các cách sử dụng bộ nhớ hiệu quả hơn trong phần sau, mong rằng những thông tin này sẽ có ích với ứng dụng của ban.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí