วิธีรับการใช้งานหน่วยความจำปัจจุบันใน Android


108

ฉันใช้ / proc / meminfo และการตอบสนองคำสั่งที่แยกวิเคราะห์แล้วอย่างไรก็ตามผลลัพธ์แสดงให้เห็นว่า:

MemTotal: 94348 kB MemFree: 5784 kB

หมายถึง. มันแสดงให้เห็นว่ามีหน่วยความจำว่างเพียง 5MB เป็นไปได้ไหมกับมือถือ android? มีเพียง 5-6 แอปพลิเคชันที่ติดตั้งบนมือถือของฉันและไม่มีงานอื่นกำลังทำงานอยู่ แต่คำสั่งนี้ยังคงแสดงว่ามีหน่วยความจำว่างน้อยมาก

ใครสามารถชี้แจงเรื่องนี้ได้บ้าง? หรือมีวิธีอื่นในการใช้หน่วยความจำใน Android หรือไม่?


2
คุณกำลังพยายามดูหน่วยความจำว่างต่ออุปกรณ์หรือต่อแอป Debug.getNativeHeapFreeSize()หากต่อแอปแล้วมันจะต้องมีการคำนวณในกองที่-la
IgorGanapolsky

1
ในการคำนวณหน่วยความจำฟรี (ในแรม) โดยใช้ / proc / meminfo คุณต้องได้รับรวมของMemFree , บัฟเฟอร์ , แคชและSwapCached มี API สำหรับวัตถุประสงค์นี้โดย Android ซึ่งทำงานบน API 16 และบนวอร์ด Meminfo มีประโยชน์หากคุณกำหนดเป้าหมาย API รุ่นเก่า
AB

คำตอบ:


174

ข้อควรระวัง: คำตอบนี้จะวัดการใช้หน่วยความจำ / ที่มีอยู่ของ DEVICE นี่ไม่ใช่สิ่งที่มีให้ในแอปของคุณ ในการวัดสิ่งที่ app ของคุณจะทำและได้รับอนุญาตให้ทำใช้หุ่นยนต์คำตอบของนักพัฒนา


เอกสาร Android - ActivityManager.MemoryInfo

  1. คำสั่ง parse / proc / meminfo คุณสามารถค้นหารหัสอ้างอิงได้ที่นี่:การใช้หน่วยความจำใน Android

  2. ใช้โค้ดด้านล่างและรับ RAM ปัจจุบัน:

    MemoryInfo mi = new MemoryInfo();
    ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    activityManager.getMemoryInfo(mi);
    double availableMegs = mi.availMem / 0x100000L;
    
    //Percentage can be calculated for API 16+
    double percentAvail = mi.availMem / (double)mi.totalMem * 100.0;

คำอธิบายของหมายเลข 0x100000L

1024 bytes      == 1 Kibibyte 
1024 Kibibyte   == 1 Mebibyte

1024 * 1024     == 1048576
1048576         == 0x100000

ค่อนข้างชัดเจนว่าตัวเลขนี้ใช้ในการแปลงจากไบต์เป็นเมบิไบต์

PS: เราต้องคำนวณหน่วยความจำทั้งหมดเพียงครั้งเดียว ดังนั้นจุดโทร 1 เพียงครั้งเดียวในรหัสของคุณจากนั้นคุณสามารถเรียกรหัสของจุดที่ 2 ซ้ำ ๆ


ฉันต้องการตรวจสอบขนาดหน่วยความจำ MemoryInfo คืออะไร?
Piraba

PIraba คลาส Android API ตรวจสอบได้ที่นี่developer.android.com/reference/android/app/… .
Badal

@SanjayJoshi นั่นเป็นเพราะตัวแปร AvailMem มีหน่วยความจำเป็นไบต์ 1024 ไบต์เท่ากับ 1 KiloByte และ 1024 กิโลไบต์เท่ากับ 1 MegaByte 1024 * 1024 เท่ากับ 1048576
Rolf ツ

2
แปลงเป็นสองเท่าด้านบนมิฉะนั้นเปอร์เซ็นต์ความพร้อมใช้งานจะเป็น 0
blueether

1
@Rolf ツขออภัย แต่ 1024 ไบต์เท่ากับหนึ่ง Kibibyte และ 1024 Kibibyte เป็นหนึ่ง MibiByte Kilo และ Mega เป็นคำนำหน้าทศนิยม 1,000 ไบต์ = 1 กิโลไบต์ นอกจากนี้ยังอธิบายผิดในคำตอบ
JacksOnF1re

88

ขึ้นอยู่กับคำจำกัดความของคุณว่าคุณต้องการรับแบบสอบถามหน่วยความจำใด


โดยปกติคุณต้องการทราบสถานะของหน่วยความจำฮีปเนื่องจากหากใช้หน่วยความจำมากเกินไปคุณจะได้รับ OOM และทำให้แอปหยุดทำงาน

สำหรับสิ่งนี้คุณสามารถตรวจสอบค่าถัดไป:

final Runtime runtime = Runtime.getRuntime();
final long usedMemInMB=(runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB=runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;

ยิ่งตัวแปร "usedMemInMB" เข้าใกล้ "maxHeapSizeInMB" มากเท่าไหร่ availHeapSizeInMBศูนย์มากขึ้นเท่านั้น (เนื่องจากการกระจายตัวของหน่วยความจำคุณอาจได้รับ OOM ก่อนที่จะถึงศูนย์)

นั่นคือสิ่งที่เครื่องมือ DDMS แสดงการใช้หน่วยความจำ


อีกวิธีหนึ่งคือการใช้ RAM จริงซึ่งเป็นจำนวนที่ทั้งระบบใช้ - ดูคำตอบที่ยอมรับเพื่อคำนวณ


อัปเดต: เนื่องจาก Android O ทำให้แอปของคุณใช้ RAM แบบเนทีฟด้วย (อย่างน้อยสำหรับพื้นที่เก็บข้อมูล Bitmaps ซึ่งโดยปกติจะเป็นสาเหตุหลักของการใช้หน่วยความจำขนาดใหญ่) ไม่ใช่แค่ฮีปเท่านั้นสิ่งต่าง ๆ ก็เปลี่ยนไปและคุณจะได้รับ OOM น้อยลง (เนื่องจาก ฮีปไม่มีบิตแมปอีกต่อไปโปรดตรวจสอบที่นี่ ) แต่คุณควรจับตาดูการใช้หน่วยความจำหากคุณสงสัยว่าหน่วยความจำรั่ว ใน Android O หากคุณมีการรั่วไหลของหน่วยความจำซึ่งน่าจะทำให้เกิด OOM ในเวอร์ชันเก่าดูเหมือนว่ามันจะพังโดยที่คุณไม่สามารถจับได้ วิธีตรวจสอบการใช้หน่วยความจำมีดังนี้

val nativeHeapSize = Debug.getNativeHeapSize()
val nativeHeapFreeSize = Debug.getNativeHeapFreeSize()
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize

แต่ฉันเชื่อว่าควรใช้ตัวสร้างโปรไฟล์ของ IDE ซึ่งแสดงข้อมูลแบบเรียลไทม์โดยใช้กราฟ

ดังนั้นข่าวดีสำหรับ Android O ก็คือการล่มยากขึ้นเนื่องจาก OOM จัดเก็บบิตแมปที่มีขนาดใหญ่มากเกินไป แต่ข่าวร้ายก็คือฉันไม่คิดว่าจะเป็นไปได้ที่จะพบกรณีเช่นนี้ในระหว่างรันไทม์


แก้ไข: ดูเหมือนว่าจะมีDebug.getNativeHeapSize()การเปลี่ยนแปลงตลอดเวลาเนื่องจากแสดงหน่วยความจำสูงสุดทั้งหมดสำหรับแอปของคุณ ดังนั้นฟังก์ชันเหล่านี้จึงใช้สำหรับผู้สร้างโปรไฟล์เท่านั้นเพื่อแสดงว่าแอปของคุณใช้งานมากเพียงใด

หากคุณต้องการรับ RAM จริงทั้งหมดและพร้อมใช้งานให้ใช้สิ่งนี้:

val memoryInfo = ActivityManager.MemoryInfo()
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(memoryInfo)
val nativeHeapSize = memoryInfo.totalMem
val nativeHeapFreeSize = memoryInfo.availMem
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize
Log.d("AppLog", "total:${Formatter.formatFileSize(this, nativeHeapSize)} " +
        "free:${Formatter.formatFileSize(this, nativeHeapFreeSize)} " +
        "used:${Formatter.formatFileSize(this, usedMemInBytes)} ($usedMemInPercentage%)")

ว้าว. เรียบง่าย แต่เป็นจริง!
วิ่ง

การใช้งานหน่วยความจำที่แท้จริงคืออะไร? ใช้ดังนั้น MemInMB ในกรณีนี้ไม่ใช่การใช้หน่วยความจำจริงของแอพ? เมื่อฉันใช้รหัสนี้มันแสดงให้ฉันเห็นการใช้งานเหมือน 50mb แต่เมื่อฉันไปที่การตั้งค่าโทรศัพท์และดูการใช้หน่วยความจำที่นั่นแอพของฉันแสดง 100mb ทำไมถึงแตกต่างกัน?
batmaci

1
@batmaci หน่วยความจำ Heap เป็นเพียงส่วนหนึ่งของการใช้หน่วยความจำทั้งหมดของแอป นอกจากนี้ยังมีการใช้หน่วยความจำแบบเนทีฟซึ่งมักจะใช้สำหรับหน้าเว็บเกมและวัตถุประสงค์ที่หนักหน่วง โดยปกติแอปจะต้องดูเฉพาะในหน่วยความจำฮีปเท่านั้นเนื่องจากค่อนข้างต่ำเมื่อเทียบกับ RAM ของอุปกรณ์และหากเข้าถึงได้แอปจะหยุดทำงาน (แม้ว่าจะมี RAM ว่างมากมายก็ตาม)
นักพัฒนา Android

เป็นข้อมูลโค้ดที่สมบูรณ์แบบมีประโยชน์มากสำหรับการตรวจสอบ OOM ขอบคุณมาก
aolphn

@AlphaOF ขอบคุณ แต่สิ่งต่างๆได้เปลี่ยนไปใน Android O ฉันได้อัปเดตคำตอบเพื่อให้เข้ากับสถานการณ์ที่นั่นแล้ว
นักพัฒนา Android

29

นี่คือวิธีคำนวณการใช้หน่วยความจำของแอปพลิเคชันที่กำลังทำงานอยู่ :

public static long getUsedMemorySize() {

    long freeSize = 0L;
    long totalSize = 0L;
    long usedSize = -1L;
    try {
        Runtime info = Runtime.getRuntime();
        freeSize = info.freeMemory();
        totalSize = info.totalMemory();
        usedSize = totalSize - freeSize;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return usedSize;

}

4
เป็นวิธีการง่ายๆ แต่ตามที่ระบุไว้ในเอกสารประกอบเมธอดคลาส Runtime freeMemory () จะส่งคืนหน่วยความจำที่มีอยู่สำหรับโปรแกรมหรือแอปพลิเคชันปัจจุบัน ดังนั้นควรระวังในขณะที่ใช้
Aksel Fatih

2
@ ปีเตอร์ - ใช่มัน "ผิด" ที่ตอบคำถามที่แตกต่างจากที่ถาม ในทางกลับกันนี่เป็นสิ่งที่ "ถูกต้อง" สำหรับสิ่งที่นักพัฒนาแอปมักจะต้องรู้: แทบไม่สำคัญว่าสถานะหน่วยความจำโดยรวมบน DEVICE คืออะไร - หากผู้ใช้ใช้งานแอปจำนวนมากระบบปฏิบัติการควรใช้ประโยชน์สูงสุดจาก หน่วยความจำ - มิฉะนั้นจะไม่มีประสิทธิภาพ OS จำเป็นต้องรู้ว่าคำตอบที่ยอมรับนั้นให้อะไรเพื่อให้รู้ว่าเมื่อใดควรเริ่มฆ่าแอปที่ไม่ได้ใช้งานเมื่อเร็ว ๆ นี้ แต่โปรแกรมเมอร์แอปจำเป็นต้องรู้ว่าคำตอบนี้ (และคำตอบที่คล้ายกันของผู้พัฒนา Android) บอกอะไรคุณเทียบกับ runtime.maxMemory
ToolmakerSteve

โซลูชันนี้ใช้งานได้กับหน่วยความจำที่สัมผัสกับแอปโดยรันไทม์เท่านั้น สิ่งนี้ไม่ได้ให้ข้อมูลเชิงลึกเกี่ยวกับหน่วยความจำระบบทั้งหมดตามที่ OP ต้องการ
AB

ในอุปกรณ์จำนวนมาก (เช่น Xiaomi) Runtime.freeMemory()จะคืนค่า 0 และRuntime.totalMemory()ส่งคืนหน่วยความจำที่จัดสรรในปัจจุบันเท่านั้น
artem

17

อีกวิธีหนึ่ง (ปัจจุบันแสดงฟรี 25MB บน G1 ของฉัน):

MemoryInfo mi = new MemoryInfo();
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
long availableMegs = mi.availMem / 1048576L;

เฮ้อเล็กซ์ขอบคุณมากสำหรับความช่วยเหลือ! อีก 1 คำถาม รหัสนี้ทำให้ฉันมี RAM ฉันต้องการแสดง Total RAM ด้วย จะไปได้อย่างไร?
Badal

@Badal ฉันไม่รู้จัก Java API สำหรับสิ่งนั้น ติดการแยกวิเคราะห์ / proc / meminfo
yanchenko

12

ปรัชญาการจัดการหน่วยความจำของ Linux คือ "หน่วยความจำว่างคือหน่วยความจำที่สูญเปล่า"

ฉันคิดว่าสองบรรทัดถัดไปจะแสดงว่าหน่วยความจำอยู่ใน "บัฟเฟอร์" เท่าใดและ "แคช" เป็นเท่าใด แม้ว่าทั้งสองจะมีความแตกต่างกัน (โปรดอย่าถามว่าความแตกต่างนั้นคืออะไร :) ทั้งคู่รวมกันโดยประมาณตามจำนวนหน่วยความจำที่ใช้ในการแคชข้อมูลไฟล์และข้อมูลเมตา

คำแนะนำที่มีประโยชน์มากกว่าสำหรับหน่วยความจำว่างบนระบบ Linux คือfree(1)คำสั่ง บนเดสก์ท็อปของฉันมันรายงานข้อมูลดังนี้:

ฟรี $ -m
             แคชบัฟเฟอร์ที่ใช้ร่วมกันฟรีที่ใช้ทั้งหมด
บันทึก: 5980 1055 4924 0 91374
- / + บัฟเฟอร์ / แคช: 589 5391
แลกเปลี่ยน: 6347 0 6347

+/- บัฟเฟอร์ / แคช: line เป็นสายเวทย์รายงานว่าฉันมีหน่วยความจำกระบวนการที่ต้องการใช้งานจริงประมาณ 589 megs และหน่วยความจำ 'ฟรี' ประมาณ 5391 megs ในแง่ที่ 91 + 374 เมกะไบต์ ของบัฟเฟอร์ / หน่วยความจำแคชสามารถถูกโยนทิ้งไปได้หากหน่วยความจำนั้นสามารถนำไปใช้ที่อื่นได้อย่างมีกำไรมากขึ้น

(เครื่องของฉันใช้งานได้ประมาณสามชั่วโมงโดยแทบจะไม่ทำอะไรเลยนอกจาก stackoverflow ซึ่งเป็นสาเหตุที่ทำให้ฉันมีหน่วยความจำว่างมาก)

หาก Android ไม่ได้มาพร้อมกับfree(1)คุณสามารถคำนวณ/proc/meminfoไฟล์ด้วยตัวเอง ฉันชอบfree(1)รูปแบบผลลัพธ์ :)


1
@ อิกอร์แล้วคุณจะต้องการcat /proc/meminfoแทน มันละเอียดกว่ามาก แต่MemFree. BuffersและCachedน่าจะเป็นเส้นที่สำคัญที่สุด
sarnold

6

ฉันอ้างอิงงานเขียนไม่กี่ชิ้น

เอกสารอ้างอิง:

เมธอด getMemorySize () นี้จะส่งคืน MemorySize ที่มีขนาดหน่วยความจำทั้งหมดและว่าง
ฉันไม่เชื่อรหัสนี้อย่างสมบูรณ์แบบ
โค้ดนี้กำลังทดสอบบน LG G3 cat.6 (v5.0.1)

    private MemorySize getMemorySize() {
        final Pattern PATTERN = Pattern.compile("([a-zA-Z]+):\\s*(\\d+)");

        MemorySize result = new MemorySize();
        String line;
        try {
            RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
            while ((line = reader.readLine()) != null) {
                Matcher m = PATTERN.matcher(line);
                if (m.find()) {
                    String name = m.group(1);
                    String size = m.group(2);

                    if (name.equalsIgnoreCase("MemTotal")) {
                        result.total = Long.parseLong(size);
                    } else if (name.equalsIgnoreCase("MemFree") || name.equalsIgnoreCase("Buffers") ||
                            name.equalsIgnoreCase("Cached") || name.equalsIgnoreCase("SwapFree")) {
                        result.free += Long.parseLong(size);
                    }
                }
            }
            reader.close();

            result.total *= 1024;
            result.free *= 1024;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    private static class MemorySize {
        public long total = 0;
        public long free = 0;
    }

ฉันรู้ว่า Pattern.compile () มีราคาแพงดังนั้นคุณสามารถย้ายรหัสไปยังสมาชิกชั้นเรียนได้


3

ฉันดูที่ Source Tree ของ Android

ภายใน com.android.server.am ActivityManagerService.java (บริการภายในที่เปิดเผยโดย android.app. ActivityManager )

public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
    final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
    final long hiddenAppMem = mProcessList.getMemLevel(ProcessList.HIDDEN_APP_MIN_ADJ);
    outInfo.availMem = Process.getFreeMemory();
    outInfo.totalMem = Process.getTotalMemory();
    outInfo.threshold = homeAppMem;
    outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2));
    outInfo.hiddenAppThreshold = hiddenAppMem;
    outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
            ProcessList.SERVICE_ADJ);
    outInfo.visibleAppThreshold = mProcessList.getMemLevel(
            ProcessList.VISIBLE_APP_ADJ);
    outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
            ProcessList.FOREGROUND_APP_ADJ);
}

ภายใน android.os. Process.java

/** @hide */
public static final native long getFreeMemory();

/** @hide */
public static final native long getTotalMemory();

เรียกวิธีการ JNI จากandroid_util_Process.cpp

สรุป

MemoryInfo.availMem = MemFree + Cached ใน / proc / meminfo

หมายเหตุ

เพิ่มหน่วยความจำทั้งหมดใน API ระดับ 16


1

คุณยังสามารถใช้เครื่องมือ DDMS ซึ่งเป็นส่วนหนึ่งของ Android SDK ได้ด้วยตัวเอง ช่วยในการจัดสรรหน่วยความจำของโค้ด java และโค้ด c / c ++ ดั้งเดิมด้วย


0
public static boolean isAppInLowMemory(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);

    return memoryInfo.lowMemory;
}

0
final long usedMemInMB=(runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB=runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;

มันเป็นรหัสที่แปลก มันส่งคืน MaxMemory - (totalMemory - freeMemory) ถ้า freeMemory เท่ากับ 0 โค้ดจะส่งคืน MaxMemory - totalMemory ดังนั้นจึงสามารถมากกว่าหรือเท่ากับ 0 ทำไมจึงไม่ใช้ freeMemory


0

นี่เป็นอีกวิธีหนึ่งในการดูการใช้งานหน่วยความจำของแอป:

adb shell dumpsys meminfo <com.package.name> -d

ตัวอย่างผลลัพธ์:

Applications Memory Usage (kB):
Uptime: 2896577 Realtime: 2896577

** MEMINFO in pid 2094 [com.package.name] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap     3472     3444        0        0     5348     4605      102
  Dalvik Heap     2349     2188        0        0     4640     4486      154
 Dalvik Other     1560     1392        0        0
        Stack      772      772        0        0
    Other dev        4        0        4        0
     .so mmap     2749     1040     1220        0
    .jar mmap        1        0        0        0
    .apk mmap      218        0       32        0
    .ttf mmap       38        0        4        0
    .dex mmap     3161       80     2564        0
   Other mmap        9        4        0        0
      Unknown       76       76        0        0
        TOTAL    14409     8996     3824        0     9988     9091      256

 Objects
               Views:       30         ViewRootImpl:        2
         AppContexts:        4           Activities:        2
              Assets:        2        AssetManagers:        2
       Local Binders:       17        Proxy Binders:       21
    Death Recipients:        7
     OpenSSL Sockets:        0

 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

สำหรับการใช้งานหน่วยความจำโดยรวม:

adb shell dumpsys meminfo

https://developer.android.com/studio/command-line/dumpsys#meminfo

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.