Làm cách nào để tìm bộ nhớ được sử dụng trên ứng dụng Android của tôi, theo lập trình?
Tôi hy vọng có một cách để làm điều đó. Ngoài ra, làm thế nào để tôi có được bộ nhớ miễn phí của điện thoại?
Làm cách nào để tìm bộ nhớ được sử dụng trên ứng dụng Android của tôi, theo lập trình?
Tôi hy vọng có một cách để làm điều đó. Ngoài ra, làm thế nào để tôi có được bộ nhớ miễn phí của điện thoại?
Câu trả lời:
Lưu ý rằng việc sử dụng bộ nhớ trên các hệ điều hành hiện đại như Linux là một lĩnh vực cực kỳ phức tạp và khó hiểu. Trong thực tế, khả năng bạn thực sự diễn giải chính xác bất cứ con số nào bạn nhận được là cực kỳ thấp. (Khá nhiều mỗi khi tôi nhìn vào số lượng sử dụng bộ nhớ với các kỹ sư khác, luôn có một cuộc thảo luận dài về ý nghĩa thực sự của chúng mà chỉ dẫn đến một kết luận mơ hồ.)
Lưu ý: hiện tại chúng tôi có nhiều tài liệu mở rộng hơn về Quản lý bộ nhớ ứng dụng của bạn bao gồm nhiều tài liệu ở đây và cập nhật hơn với trạng thái của Android.
Điều đầu tiên có lẽ là đọc phần cuối của bài viết này có một số thảo luận về cách quản lý bộ nhớ trên Android:
Thay đổi API dịch vụ bắt đầu với Android 2.0
Bây giờ ActivityManager.getMemoryInfo()
là API cấp cao nhất của chúng tôi để xem xét việc sử dụng bộ nhớ tổng thể. Điều này chủ yếu là để giúp một ứng dụng đánh giá mức độ gần của hệ thống để không còn bộ nhớ cho các quy trình nền, do đó cần phải bắt đầu tiêu diệt các quy trình cần thiết như dịch vụ. Đối với các ứng dụng Java thuần túy, điều này sẽ ít được sử dụng, vì giới hạn vùng heap Java có một phần để tránh một ứng dụng không thể làm căng thẳng hệ thống đến thời điểm này.
Ở cấp độ thấp hơn, bạn có thể sử dụng API gỡ lỗi để nhận thông tin cấp độ hạt nhân thô về việc sử dụng bộ nhớ: android.os.Debug.MemoryInfo
Lưu ý bắt đầu với 2.0 cũng có API ActivityManager.getProcessMemoryInfo
, để có được thông tin này về một quy trình khác: ActivityManager.getProcessMemoryInfo (int [])
Điều này trả về cấu trúc MemoryInfo cấp thấp với tất cả các dữ liệu này:
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;
/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
Nhưng như những gì khác biệt là giữa Pss
, PrivateDirty
và SharedDirty
... cũng tại những niềm vui bắt đầu.
Rất nhiều bộ nhớ trong Android (và các hệ thống Linux nói chung) thực sự được chia sẻ qua nhiều quy trình. Vì vậy, bao nhiêu bộ nhớ mà một quá trình sử dụng là thực sự không rõ ràng. Thêm vào đầu trang phân trang đó ra đĩa (huống chi là trao đổi mà chúng ta không sử dụng trên Android) và nó thậm chí còn chưa rõ ràng.
Do đó, nếu bạn lấy tất cả RAM vật lý thực sự được ánh xạ vào từng quy trình và cộng tất cả các quy trình, có thể bạn sẽ kết thúc với số lượng lớn hơn nhiều so với tổng RAM thực tế.
Các Pss
số là một thước đo các tính kernel mà sẽ đưa vào chia sẻ bộ nhớ tài khoản - về cơ bản mỗi trang của RAM trong một quá trình được thu nhỏ bằng một tỷ số giữa số quy trình khác cũng sử dụng trang đó. Bằng cách này, bạn có thể (về lý thuyết) cộng pss trên tất cả các quy trình để xem tổng RAM họ đang sử dụng và so sánh pss giữa các quy trình để có được ý tưởng sơ bộ về trọng lượng tương đối của chúng.
Một số liệu thú vị khác ở đây là PrivateDirty
, về cơ bản là lượng RAM bên trong quá trình không thể phân trang vào đĩa (nó không được hỗ trợ bởi cùng một dữ liệu trên đĩa) và không được chia sẻ với bất kỳ quy trình nào khác. Một cách khác để xem xét điều này là RAM sẽ có sẵn cho hệ thống khi quá trình đó biến mất (và có thể nhanh chóng bị chìm vào bộ nhớ cache và các ứng dụng khác của nó).
Đó là khá nhiều API SDK cho việc này. Tuy nhiên, có nhiều hơn bạn có thể làm như một nhà phát triển với thiết bị của bạn.
Sử dụng adb
, có rất nhiều thông tin bạn có thể nhận được về việc sử dụng bộ nhớ của một hệ thống đang chạy. Một phổ biến là lệnh adb shell dumpsys meminfo
sẽ đưa ra một loạt thông tin về việc sử dụng bộ nhớ của mỗi quy trình Java, có chứa thông tin trên cũng như nhiều thứ khác. Bạn cũng có thể giải quyết tên hoặc pid của một quy trình duy nhất để xem, ví dụ, adb shell dumpsys meminfo system
đưa cho tôi quy trình hệ thống:
** MEMINFO trong pid 890 [hệ thống] ** tổng cộng dalvik khác kích thước: 10940 7047 N / A 17987 phân bổ: 8943 5516 N / A 14459 miễn phí: 336 1531 N / A 1867 (Pss): 4585 9282 11916 25783 (chia sẻ bẩn): 2184 3596 916 6696 (bẩn): 4504 5956 7456 17916 Các đối tượng Lượt xem: 149 Lượt xem: 4 AppContexts: 13 Hoạt động: 0 Tài sản: 4 Tài sản quản lý: 4 Chất kết dính cục bộ: 141 Chất kết dính proxy: 158 Người nhận tử vong: 49 Ổ cắm OpenSSL: 0 SQL đống: 205 dbFiles: 0 numPager: 0 inactivePageKB: 0 activePageKB: 0
Phần trên cùng là một trong những chính, nơi size
là tổng kích thước trong không gian địa chỉ của một đống Đặc biệt, allocated
là kb phân bổ thực tế rằng đống nghĩ nó có, free
là kb còn lại giải phóng đống có cho bổ sung cân đối, và pss
và priv dirty
đều giống nhau như đã thảo luận trước khi cụ thể đến các trang được liên kết với mỗi đống.
Nếu bạn chỉ muốn xem việc sử dụng bộ nhớ trong tất cả các quy trình, bạn có thể sử dụng lệnh adb shell procrank
. Đầu ra của điều này trên cùng một hệ thống trông giống như:
PID Vss Rss Pss Sử dụng cmdline 890 84456K 48668K 25850K 21284K hệ thống máy chủ 1231 50748K 39088K 17587K 13792K com.android.launcher2 947 34488K 28528K 10834K 9308K com.android.wallapers 987 26964K 26956K 8751K 7308K com.google. Process.gapps 954 24300K 24296K 6249K 4824K com.android.phone 948 23020K 23016K 5864K 4748K com.android.inputmethod.latin Hợp tử 888 25728K 25724K 5774K 3668K 977 24100K 24096K 5667K 4340K android. Process.acore ... 59 336K 332K 99K 92K / hệ thống / thùng / lượt cài đặt 60 396K 392K 93K 84K / hệ thống / thùng / kho khóa 51 280K 276K 74K 68K / hệ thống / thùng / nhân viên phục vụ 54 256K 252K 69K 64K / hệ thống / thùng / gỡ lỗi
Ở đây Vss
, Rss
các cột và các cột về cơ bản là nhiễu (đây là không gian địa chỉ thẳng và mức sử dụng RAM của một quy trình, trong đó nếu bạn thêm mức sử dụng RAM trên các quy trình, bạn sẽ nhận được một số lượng lớn một cách lố bịch).
Pss
là như chúng ta đã thấy trước đây, và Uss
là Priv Dirty
.
Điều thú vị cần lưu ý ở đây: Pss
và Uss
hơi khác (hoặc hơn một chút) khác với những gì chúng ta đã thấy meminfo
. Tại sao vậy? Vâng procrank sử dụng một cơ chế nhân khác để thu thập dữ liệu của nó so với meminfo
thực tế và chúng cho kết quả hơi khác nhau. Tại sao vậy? Thành thật mà nói tôi không có manh mối. Tôi tin rằng procrank
có thể chính xác hơn ... nhưng thực sự, điều này chỉ để lại quan điểm: "lấy bất kỳ thông tin bộ nhớ nào bạn nhận được bằng một hạt muối; thường là một hạt rất lớn."
Cuối cùng, có lệnh adb shell cat /proc/meminfo
đưa ra một bản tóm tắt về việc sử dụng bộ nhớ tổng thể của hệ thống. Có rất nhiều dữ liệu ở đây, chỉ một vài con số đầu tiên đáng để thảo luận (và những con số còn lại được ít người hiểu và câu hỏi của tôi về những người đó về chúng thường dẫn đến những giải thích mâu thuẫn):
MemTotal: 395144 kB MemFree: 184936 kB Bộ đệm: 880 kB Đã lưu: 84104 kB Hoán đổi: 0 kB
MemTotal
là tổng dung lượng bộ nhớ khả dụng cho kernel và dung lượng người dùng (thường ít hơn RAM vật lý thực tế của thiết bị, vì một số RAM đó là cần thiết cho radio, bộ đệm DMA, v.v.).
MemFree
là dung lượng RAM hoàn toàn không được sử dụng. Con số bạn thấy ở đây rất cao; thông thường trên hệ thống Android, đây sẽ chỉ là một vài MB, vì chúng tôi cố gắng sử dụng bộ nhớ khả dụng để duy trì hoạt động của các quy trình
Cached
là RAM được sử dụng cho bộ đệm hệ thống tập tin và những thứ khác. Các hệ thống điển hình sẽ cần phải có 20 MB hoặc hơn để tránh tình trạng phân trang xấu; Trình diệt bộ nhớ Android được điều chỉnh cho một hệ thống cụ thể để đảm bảo rằng các tiến trình nền bị hủy trước khi RAM được lưu trữ quá nhiều bởi chúng dẫn đến việc phân trang như vậy.
Có, bạn có thể lấy thông tin bộ nhớ theo chương trình và quyết định có thực hiện công việc đòi hỏi nhiều bộ nhớ hay không.
Nhận VM Heap Size bằng cách gọi:
Runtime.getRuntime().totalMemory();
Nhận bộ nhớ VM được phân bổ bằng cách gọi:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Nhận giới hạn kích thước heap VM bằng cách gọi:
Runtime.getRuntime().maxMemory()
Nhận bộ nhớ phân bổ riêng bằng cách gọi:
Debug.getNativeHeapAllocatedSize();
Tôi đã tạo một ứng dụng để tìm ra hành vi OutOfMemoryError và theo dõi việc sử dụng bộ nhớ.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Bạn có thể lấy mã nguồn tại https://github.com/coocood/oom-research
Đây là một công việc đang tiến triển, nhưng đây là điều tôi không hiểu:
ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );
List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}
Collection<Integer> keys = pidMap.keySet();
for(int key : keys)
{
int pids[] = new int[1];
pids[0] = key;
android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
{
Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
}
}
Tại sao PID không được ánh xạ tới kết quả trong ActivityManager.getProcessMemoryInfo ()? Rõ ràng bạn muốn làm cho dữ liệu kết quả có ý nghĩa, vậy tại sao Google làm cho nó rất khó để tương quan kết quả? Hệ thống hiện tại thậm chí không hoạt động tốt nếu tôi muốn xử lý toàn bộ việc sử dụng bộ nhớ vì kết quả trả về là một mảng các đối tượng android.os.Debug.MemoryInfo, nhưng không có đối tượng nào thực sự cho bạn biết chúng có liên quan gì đến chúng. Nếu bạn chỉ đơn giản vượt qua trong một mảng của tất cả các pids, bạn sẽ không có cách nào để hiểu kết quả. Theo tôi hiểu, nó được sử dụng, nó khiến cho việc truyền nhiều hơn một pid cùng một lúc trở nên vô nghĩa, và nếu đó là trường hợp, tại sao làm cho nó hoạt động như vậy thì trình quản lý.getProcessMemoryInfo () chỉ mất một mảng int?
Hackbod là một trong những câu trả lời hay nhất về Stack Overflow. Nó ném ánh sáng vào một chủ đề rất tối. Nó đã giúp tôi rất nhiều.
Một tài nguyên thực sự hữu ích khác là video phải xem này: Google I / O 2011: Quản lý bộ nhớ cho các ứng dụng Android
CẬP NHẬT:
Thống kê quy trình, một dịch vụ để khám phá cách ứng dụng của bạn quản lý bộ nhớ được giải thích tại bài đăng trên blog Chỉ số quy trình: Hiểu cách ứng dụng của bạn sử dụng RAM của Dianne Hackborn:
Android Studio 0.8.10+ đã giới thiệu một công cụ cực kỳ hữu ích có tên Memory Monitor .
Những gì nó tốt cho:
- Hiển thị bộ nhớ khả dụng và được sử dụng trong biểu đồ và các sự kiện thu gom rác theo thời gian.
- Nhanh chóng kiểm tra xem sự chậm chạp của ứng dụng có thể liên quan đến các sự kiện thu gom rác quá mức hay không.
- Nhanh chóng kiểm tra xem sự cố ứng dụng có thể liên quan đến việc hết bộ nhớ hay không.
Hình 1. Buộc một sự kiện GC (Bộ sưu tập rác) trên Trình theo dõi bộ nhớ Android
Bạn có thể có nhiều thông tin tốt về mức tiêu thụ thời gian thực RAM của ứng dụng bằng cách sử dụng nó.
1) Tôi đoán là không, ít nhất là không phải từ Java.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Chúng tôi phát hiện ra rằng tất cả các cách tiêu chuẩn để có được tổng bộ nhớ của quy trình hiện tại có một số vấn đề.
Runtime.getRuntime().totalMemory()
: chỉ trả về bộ nhớ JVMActivityManager.getMemoryInfo()
, Process.getFreeMemory()
Và bất cứ điều gì khác dựa trên /proc/meminfo
- trở về thông tin bộ nhớ về tất cả các quá trình kết hợp (ví dụ android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- chỉ sử dụng mallinfo()
thông tin trả về phân bổ bộ nhớ được thực hiện bởi malloc()
và chỉ các chức năng liên quan (xem android_os_Debug.cpp )Debug.getMemoryInfo()
- thực hiện công việc nhưng quá chậm. Mất khoảng 200ms trên Nexus 6 cho một cuộc gọi. Chi phí hoạt động làm cho chức năng này trở nên vô dụng đối với chúng tôi khi chúng tôi gọi nó thường xuyên và mọi cuộc gọi đều khá đáng chú ý (xem android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- gọi Debug.getMemoryInfo()
nội bộ (xem ActivityManagerService.java )Cuối cùng, chúng tôi đã kết thúc bằng cách sử dụng mã sau đây:
const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);
if( statsArr.Length < 2 )
throw new Exception("Parsing error of /proc/self/statm: " + stats);
return long.Parse(statsArr[1]) * pageSize;
Nó trả về số liệu VmRSS . Bạn có thể tìm thêm chi tiết về nó ở đây: một , hai và ba .
PS Tôi nhận thấy rằng chủ đề vẫn còn thiếu một đoạn mã thực tế và đơn giản về cách ước tính mức sử dụng bộ nhớ riêng của quy trình nếu hiệu suất không phải là một yêu cầu quan trọng:
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
res += memInfo.getTotalPrivateClean();
return res * 1024L;
Trong android studio 3.0, họ đã giới thiệu android-profiler để giúp bạn hiểu cách ứng dụng của bạn sử dụng tài nguyên CPU, bộ nhớ, mạng và pin.
https://developer.android.com/studio/profile/android-profiler
Có rất nhiều câu trả lời ở trên chắc chắn sẽ giúp bạn nhưng (sau 2 ngày đủ khả năng và nghiên cứu về các công cụ bộ nhớ adb) Tôi nghĩ rằng tôi có thể giúp với ý kiến của mình .
Như Hackbod nói: Do đó, nếu bạn lấy tất cả RAM vật lý thực sự được ánh xạ vào từng quy trình và cộng tất cả các quy trình, có thể bạn sẽ kết thúc với số lượng lớn hơn nhiều so với tổng RAM thực tế. vì vậy không có cách nào bạn có thể có được số lượng bộ nhớ chính xác cho mỗi quá trình.
Nhưng bạn có thể đến gần nó bằng một số logic..và tôi sẽ nói như thế nào ..
Có một số API như
android.os.Debug.MemoryInfo
vàActivityManager.getMemoryInfo()
được đề cập ở trên mà bạn có thể đã được đọc và sử dụng nhưng tôi sẽ nói về cách khác
Vì vậy, trước tiên bạn cần phải là người dùng root để nó hoạt động. Nhận được vào giao diện điều khiển với đặc quyền gốc bằng cách thực hiện su
trong quá trình và nhận được nó output and input stream
. Sau đó chuyển id\n
(enter) trong ouputstream và ghi nó vào quá trình đầu ra, Nếu sẽ nhận được một luồng đầu vào có chứa uid=0
, bạn là người dùng root.
Bây giờ đây là logic mà bạn sẽ sử dụng trong quá trình trên
Khi bạn nhận được dòng thông tin của quá trình, bạn sẽ ra lệnh (procrank, dumpsys meminfo, v.v.) \n
thay vì id và lấy nó inputstream
và đọc, lưu trữ luồng theo byte [], char [] vv .. sử dụng raw thô..và bạn xong rồi !!!!!
sự cho phép
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Kiểm tra nếu bạn là người dùng root:
// su command to get root access
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream =
new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
// write id to console with enter
dataOutputStream.writeBytes("id\n");
dataOutputStream.flush();
String Uid = dataInputStream.readLine();
// read output and check if uid is there
if (Uid.contains("uid=0")) {
// you are root user
}
}
Thực hiện lệnh của bạn với su
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
// adb command
dataOutputStream.writeBytes("procrank\n");
dataOutputStream.flush();
BufferedInputStream bufferedInputStream =
new BufferedInputStream(process.getInputStream());
// this is important as it takes times to return to next line so wait
// else you with get empty bytes in buffered stream
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// read buffered stream into byte,char etc.
byte[] bff = new byte[bufferedInputStream.available()];
bufferedInputStream.read(bff);
bufferedInputStream.close();
}
}
Bạn nhận được một dữ liệu thô trong một chuỗi từ bảng điều khiển thay vì trong một số trường hợp từ bất kỳ API nào, rất phức tạp để lưu trữ vì bạn sẽ cần phải tách nó theo cách thủ công .
Đây chỉ là một thử, xin vui lòng gợi ý cho tôi nếu tôi bỏ lỡ điều gì đó