Phát hiện kích thước heap ứng dụng trong Android


145

Làm thế nào để bạn lập trình phát hiện kích thước heap ứng dụng có sẵn cho ứng dụng Android?

Tôi nghe nói có một chức năng thực hiện điều này trong các phiên bản sau của SDK. Trong mọi trường hợp, tôi đang tìm giải pháp hoạt động cho 1,5 trở lên.


Câu trả lời:


453

Có hai cách để suy nghĩ về cụm từ của bạn "kích thước heap ứng dụng có sẵn":

  1. Ứng dụng của tôi có thể sử dụng bao nhiêu heap trước khi một lỗi cứng được kích hoạt? Và

  2. Ứng dụng của tôi nên sử dụng bao nhiêu heap , với các ràng buộc của phiên bản HĐH Android và phần cứng của thiết bị người dùng?

Có một phương pháp khác nhau để xác định từng điều trên.

Đối với mục 1 ở trên: maxMemory()

có thể được gọi (ví dụ: trong onCreate()phương thức hoạt động chính của bạn ) như sau:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Phương pháp này cho bạn biết tổng số byte của ứng dụng của bạn được phép sử dụng.

Đối với mục 2 ở trên: getMemoryClass()

có thể được gọi như sau:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Phương pháp này cho bạn biết khoảng bao nhiêu megabyte mà ứng dụng của bạn nên sử dụng nếu muốn tuân thủ đúng các giới hạn của thiết bị hiện tại và về các quyền của các ứng dụng khác để chạy mà không bị ép buộc vào onStop()/ onResume()chu kỳ vì chúng thô lỗ tuôn ra khỏi bộ nhớ trong khi ứng dụng voi của bạn tắm trong bể sục Android.

Sự khác biệt này không được ghi lại rõ ràng, theo như tôi biết, nhưng tôi đã thử nghiệm giả thuyết này trên năm thiết bị Android khác nhau (xem bên dưới) và đã xác nhận với sự hài lòng của riêng tôi rằng đây là một cách giải thích chính xác.

Đối với phiên bản chứng khoán của Android, maxMemory()thông thường sẽ trả về cùng số megabyte như được chỉ định trong getMemoryClass()(nghĩa là xấp xỉ một triệu lần giá trị sau).

Tình huống duy nhất (mà tôi biết) mà hai phương pháp có thể phân kỳ là trên một thiết bị đã root chạy phiên bản Android như CyanogenMod, cho phép người dùng chọn thủ công kích thước heap cho phép cho mỗi ứng dụng. Ví dụ, trong CM, tùy chọn này xuất hiện trong "Cài đặt CyanogenMod" / "Hiệu suất" / "Kích thước heap VM".

LƯU Ý: HÃY TUYỆT VỜI R SET RÀNG THIẾT LẬP GIÁ TRỊ NÀY CÓ THỂ GỬI HỆ THỐNG CỦA BẠN, ĐẶC BIỆT nếu bạn chọn một giá trị nhỏ hơn bình thường cho thiết bị của mình.

Dưới đây là kết quả thử nghiệm của tôi cho thấy các giá trị được trả về bởi maxMemory()getMemoryClass()cho bốn thiết bị khác nhau đang chạy CyanogenMod, sử dụng hai giá trị heap khác nhau (được đặt thủ công) cho mỗi thiết bị:

  • G1:
    • Với VM Heap Size được đặt thành 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Với VM Heap Size được đặt thành 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Với VM Heap Size được đặt thành 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Với VM Heap Size được đặt thành 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Với kích thước VM Heap được đặt thành 32MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Với kích thước VM Heap được đặt thành 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewabic GTab:
    • Với VM Heap Size được đặt thành 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Với VM Heap Size được đặt thành 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

Ngoài những điều trên, tôi đã thử nghiệm trên máy tính bảng Novo7 Paladin chạy Ice Cream Sandwich. Đây thực chất là phiên bản chứng khoán của ICS, ngoại trừ việc tôi đã root máy tính bảng thông qua một quy trình đơn giản không thay thế toàn bộ HĐH và đặc biệt không cung cấp giao diện cho phép điều chỉnh kích thước heap theo cách thủ công.

Đối với thiết bị đó, đây là kết quả:

  • Tháng 7
    • maxMemory: 62914560
    • getMemoryClass: 60

Ngoài ra (theo Kishore trong một bình luận bên dưới):

  • HTC One X
    • maxMemory: 67108864
    • getMemoryClass: 64

Và (mỗi bình luận của akauppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (Không được chỉ định trong bình luận)
    • getMemoryClass: 48
    • LargeMemoryClass: 128

Mỗi bình luận từ cmcromance:

  • Galaxy S3 (Jelly Bean) heap lớn
    • maxMemory: 268435456
    • getMemoryClass: 64

Và (mỗi bình luận của tencent):

  • LG Nexus 5 (4.4.3) bình thường
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) heap lớn
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) bình thường
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) đống lớn
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Phiên bản Cửa hàng Play Galaxy S4 (4.4.2) bình thường
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) heap lớn
    • maxMemory: 536870912
    • getMemoryClass: 192

Các thiết bị khác

  • Huawei Nexus 6P (6.0.1) bình thường
    • maxMemory: 201326592
    • getMemoryClass: 192

Tôi đã không thử nghiệm hai phương pháp này bằng cách sử dụng tùy chọn kê khai đặc biệt của android: LargeHeap = "true" kể từ Honeycomb, nhưng nhờ cmcromance và tencent, chúng tôi có một số giá trị bigHeap mẫu, như đã báo cáo ở trên.

Kỳ vọng của tôi (dường như được hỗ trợ bởi các số lớn Heap ở trên) là tùy chọn này sẽ có hiệu ứng tương tự như đặt heap theo cách thủ công thông qua hệ điều hành đã được root - tức là, nó sẽ tăng giá trị maxMemory()trong khi để getMemoryClass()yên. Có một phương thức khác, getLargeMemoryClass (), cho biết số lượng bộ nhớ được phép cho một ứng dụng sử dụng cài đặt LargeHeap. Tài liệu cho getLargeMemoryClass (), "hầu hết các ứng dụng không cần dung lượng bộ nhớ này và thay vào đó nên duy trì ở giới hạn getMemoryClass ()."

Nếu tôi đoán đúng, thì sử dụng tùy chọn đó sẽ có cùng lợi ích (và nguy hiểm) như sử dụng không gian được tạo bởi người dùng đã tăng số lượng lớn thông qua hệ điều hành đã root (nghĩa là, nếu ứng dụng của bạn sử dụng bộ nhớ bổ sung, nó có thể sẽ không chơi độc đáo với bất kỳ ứng dụng nào khác mà người dùng đang chạy cùng một lúc).

Lưu ý rằng lớp bộ nhớ rõ ràng không cần phải là bội số của 8MB.

Chúng ta có thể thấy ở trên rằng getMemoryClass()kết quả không thay đổi đối với cấu hình thiết bị / HĐH nhất định, trong khi giá trị maxMemory () thay đổi khi heap được người dùng đặt khác nhau.

Kinh nghiệm thực tế của riêng tôi là trên G1 (có lớp bộ nhớ là 16), nếu tôi chọn thủ công 24MB làm kích thước heap, tôi có thể chạy mà không gặp lỗi ngay cả khi mức sử dụng bộ nhớ của tôi được phép tăng lên 20 MB (có lẽ nó có thể cao tới 24MB, mặc dù tôi chưa thử điều này). Nhưng các ứng dụng lớn tương tự khác có thể bị xóa khỏi bộ nhớ do hậu quả của sự cồng kềnh của ứng dụng của tôi. Và ngược lại, ứng dụng của tôi có thể bị xóa khỏi bộ nhớ nếu những ứng dụng bảo trì cao này được người dùng đưa lên nền trước.

Vì vậy, bạn không thể vượt quá dung lượng bộ nhớ được chỉ định bởi maxMemory(). Và, bạn nên cố gắng ở trong giới hạn được chỉ định bởi getMemoryClass(). Một cách để làm điều đó, nếu tất cả các cách khác đều thất bại, có thể là giới hạn chức năng cho các thiết bị đó theo cách bảo tồn bộ nhớ.

Cuối cùng, nếu bạn có kế hoạch vượt qua số megabyte được chỉ định trong getMemoryClass(), lời khuyên của tôi là hãy làm việc lâu dài và chăm chỉ để tiết kiệm và khôi phục trạng thái ứng dụng của bạn, để trải nghiệm của người dùng hầu như không bị gián đoạn nếu xảy ra onStop()/ onResume()chu kỳ.

Trong trường hợp của tôi, vì lý do hiệu suất, tôi giới hạn ứng dụng của mình với các thiết bị chạy 2.2 trở lên và điều đó có nghĩa là hầu hết tất cả các thiết bị chạy ứng dụng của tôi sẽ có bộ nhớ từ 24 trở lên. Vì vậy, tôi có thể thiết kế để chiếm tới 20 MB heap và cảm thấy khá tự tin rằng ứng dụng của tôi sẽ chơi tốt với các ứng dụng khác mà người dùng có thể đang chạy cùng một lúc.

Nhưng sẽ luôn có một vài người dùng đã root phiên bản Android 2.2 trở lên trên một thiết bị cũ hơn (ví dụ: G1). Khi bạn gặp phải một cấu hình như vậy, lý tưởng nhất là bạn nên giảm sử dụng bộ nhớ, ngay cả khi maxMemory()đang nói với bạn rằng bạn có thể tăng cao hơn nhiều so với 16 MB getMemoryClass()đang nói với bạn rằng bạn nên nhắm mục tiêu. Và nếu bạn không thể đảm bảo chắc chắn rằng ứng dụng của bạn sẽ nằm trong ngân sách đó, thì ít nhất hãy đảm bảo rằng onStop()/ onResume()hoạt động trơn tru.

getMemoryClass(), như được chỉ ra bởi Diane Hackborn (hackbod) ở trên, chỉ khả dụng trở lại API cấp 5 (Android 2.0), và vì vậy, như cô khuyên, bạn có thể giả sử rằng phần cứng vật lý của bất kỳ thiết bị nào chạy phiên bản HĐH trước đó đều được thiết kế để tối ưu hóa các ứng dụng chiếm một không gian lớn không quá 16 MB.

Ngược lại, maxMemory()theo các tài liệu, hiện có sẵn tất cả các cách trở lại mức API 1. maxMemory(), trên một phiên bản trước 2.0, có lẽ sẽ trả về một giá trị 16MB, nhưng tôi làm thấy rằng trong các phiên bản của tôi (nhiều hơn) CyanogenMod người dùng có thể chọn giá trị heap thấp tới 12MB, điều này có thể dẫn đến giới hạn heap thấp hơn, và vì vậy tôi khuyên bạn nên tiếp tục kiểm tra maxMemory()giá trị, ngay cả đối với các phiên bản HĐH trước 2.0. Bạn thậm chí có thể phải từ chối chạy trong trường hợp không chắc là giá trị này được đặt thậm chí thấp hơn 16MB, nếu bạn cần có nhiều hơn mức maxMemory()cho phép.


2
@Carl Xin chào Carl, Cảm ơn bạn cho bài viết tuyệt vời của bạn. Và tôi đã thử nghiệm tùy chọn, android: LargeHeap = "true" trên Galaxy S3 với JellyBean. Đây là kết quả. [maxMemory: 256.0MB, memoryClass: 64MB] (maxMemory là 64MB mà không có tùy chọn bigheap)
cmcromance

1
@Carl Haha Thật vinh dự! :)
cmcromance

1
Dành cho HTC One X: maxMemory: 67108864 memoryClass: 64
Kishore

1
LG Nexus 5 (4.4.3) (bình thường): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (heap lớn): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas

1
Tài liệu rất đẹp. Vui lòng cung cấp điều này cho Android. Tôi không thể tìm thấy tài liệu Android thích hợp như vậy
Jimit Patel 6/11/2015

20

API chính thức là:

Điều này đã được giới thiệu trong 2.0 nơi xuất hiện các thiết bị bộ nhớ lớn hơn. Bạn có thể giả sử rằng các thiết bị chạy các phiên bản trước của HĐH đang sử dụng lớp bộ nhớ gốc (16).


13

Debug.getNativeHeapSize()Tôi sẽ làm điều đó, tôi nên suy nghĩ. Nó đã ở đó kể từ 1.0.

Các Debuglớp học có rất nhiều phương pháp tuyệt vời cho việc phân bổ theo dõi và mối quan tâm hiệu suất khác. Ngoài ra, nếu bạn cần phát hiện tình huống bộ nhớ thấp, hãy kiểm tra Activity.onLowMemory().


Cảm ơn Neil. Điều này có hoạt động khi ứng dụng đang chạy với gỡ lỗi đã tắt không?
hpique

2
Tôi còn khá mới với điều này, nhưng tôi không nghĩ rằng heap bản địa giống như đống ứng dụng (Dalvik) mà câu hỏi đề cập đến. Heap riêng cung cấp sao lưu cho dữ liệu bitmap, được phân bổ theo mã gốc, trong khi heap ứng dụng chứa dữ liệu ứng dụng Java. Tôi đã quan tâm khi biết rằng heap riêng được tính theo giới hạn của heap ứng dụng và sau 3.0 phân bổ thực sự xảy ra trên heap của ứng dụng. Diane Hackborn (hackbod) đăng bài về chủ đề này tại đây: stackoverflow.com/questions/1945142/bitmaps-in-android Nhưng ngay cả đối với 3.0, cũng có dữ liệu không "gốc" trên heap ứng dụng.
Carl

1
Vì một số cập nhật Android gần đây (có thể là KitKat 4.4), bitmap nằm trong đống JVM.
akauppi

13

Đây là cách bạn làm điều đó:

Lấy kích thước heap tối đa mà ứng dụng có thể sử dụng:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Nhận bao nhiêu phần trăm mà ứng dụng của bạn hiện đang sử dụng:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Nhận bao nhiêu heap mà ứng dụng của bạn hiện có thể sử dụng (bộ nhớ khả dụng):

long availableMemory=maxMemory-usedMemory;

Và, để định dạng từng cái một cách độc đáo, bạn có thể sử dụng:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 

5

Điều này trả về kích thước heap tối đa tính bằng byte:

Runtime.getRuntime().maxMemory()

Tôi đã sử dụng ActivityManager.getMemoryClass () nhưng trên CyanogenMod 7 (tôi đã không kiểm tra nó ở nơi khác), nó trả về giá trị sai nếu người dùng đặt kích thước heap theo cách thủ công.


Vì tài liệu getMemoryClassdường như ngụ ý rằng con số có thể không giống với kích thước heap có sẵn cho vm của bạn và vì tài liệu cho getNativeHeapSizelà ... ngầm, tôi thực sự nghĩ rằng đó Runtime.getRuntime().maxMemory()là câu trả lời tốt nhất.
BoD

3

Một số hoạt động nhanh hơn java quản lý không gian heap. Trì hoãn các hoạt động trong một thời gian có thể giải phóng không gian bộ nhớ. Bạn có thể sử dụng phương pháp này để thoát lỗi kích thước heap:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}

Đã kiểm tra Tốt. Bạn có thể xây dựng hai điều sau: AvailableMemoryPercentage và MIN_AVAILABLE_MEMORY_PERCENTAGE không? vài lời giải thích?
Noor Hossain

1
AvailableMemoryPercentagetheo công thức: bao nhiêu bộ nhớ của thiết bị hiện đang miễn phí. MIN_AVAILABLE_MEMORY_PERCENTAGElà tham số tùy chỉnh của bạn, ngưỡng mà bạn bắt đầu chờ người thu gom rác thực hiện công việc của mình.
Zon

1

Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxMemory () = 201326592

Tôi đã phạm sai lầm khi tạo nguyên mẫu trò chơi của mình trên Nexus 7 và sau đó phát hiện ra nó đã hết bộ nhớ gần như ngay lập tức trên máy tính bảng 4.04 chung của vợ tôi (bộ nhớ 48, maxmemory 50331648)

Tôi sẽ cần cơ cấu lại dự án của mình để tải ít tài nguyên hơn khi tôi xác định lớp bộ nhớ thấp.
Có cách nào trong Java để xem kích thước heap hiện tại không? .


Trên Nexus7-2013 đã nói, getLargeMemoryClass () = 512.
akauppi

0

Bạn có nghĩa là lập trình, hoặc chỉ trong khi bạn đang phát triển và gỡ lỗi? Nếu sau này, bạn có thể thấy thông tin đó từ phối cảnh DDMS trong Eclipse. Khi trình giả lập của bạn (có thể cả điện thoại vật lý đã được cắm) đang chạy, nó sẽ liệt kê các quy trình hoạt động trong một cửa sổ bên trái. Bạn có thể chọn nó và có một tùy chọn để theo dõi phân bổ heap.


Ý tôi là lập trình. Làm rõ câu hỏi. Cảm ơn.
hpique

1
Tôi khuyên bạn nên xem bài đăng này của một trong những lập trình viên nền tảng Android sau đó: stackoverflow.com/questions/2298208/ Kẻ
Steve Haley

0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

giá trị là b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

giá trị là MB


Loại rtnào? Nó được tuyên bố ở đâu?
FindOut_Quran
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.