ต้องการคำอธิบายเกี่ยวกับขนาดชุดที่อยู่อาศัย / ขนาดเสมือน


61

ฉันพบว่าpidstatจะเป็นเครื่องมือที่ดีในการตรวจสอบกระบวนการ ฉันต้องการคำนวณการใช้หน่วยความจำเฉลี่ยของกระบวนการเฉพาะ นี่คือตัวอย่างผลลัพธ์:

02:34:36 PM       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
02:34:37 PM      7276      2.00      0.00  349212 210176   7.14  scalpel

(นี่เป็นส่วนหนึ่งของผลลัพธ์จากpidstat -r -p 7276)

ฉันควรใช้ข้อมูล Resident Set Size (RSS) หรือ Virtual Size (VSZ) เพื่อคำนวณปริมาณการใช้หน่วยความจำเฉลี่ยหรือไม่ ฉันได้อ่านบางสิ่งเกี่ยวกับ Wikipedia และในกระดานสนทนา แต่ฉันไม่แน่ใจว่าจะเข้าใจความแตกต่างอย่างสมบูรณ์ นอกจากนี้ดูเหมือนว่าไม่มีใครเชื่อถือได้ ดังนั้นฉันจะตรวจสอบกระบวนการเพื่อให้ได้ใช้หน่วยความจำได้อย่างไร

ความช่วยเหลือใด ๆ ในเรื่องนี้จะเป็นประโยชน์



คำตอบ:


63

RSS คือจำนวนหน่วยความจำที่กระบวนการนี้มีอยู่ในหน่วยความจำหลัก (RAM) VSZ คือจำนวนหน่วยความจำเสมือนที่กระบวนการมีทั้งหมด ซึ่งรวมถึงหน่วยความจำทุกประเภททั้งใน RAM และสลับออก ตัวเลขเหล่านี้สามารถเบ้เพราะพวกเขายังรวมถึงห้องสมุดที่ใช้ร่วมกันและหน่วยความจำประเภทอื่น ๆ คุณสามารถใช้งานได้ห้าร้อยอินสแตนซ์bashและขนาดโดยรวมของหน่วยความจำจะไม่รวมผลรวมของค่า RSS หรือ VSZ

หากคุณต้องการทราบรายละเอียดเพิ่มเติมเกี่ยวกับ footprint หน่วยความจำของกระบวนการคุณมีตัวเลือกบางอย่าง คุณสามารถผ่าน/proc/$PID/mapและกำจัดสิ่งที่คุณไม่ชอบ หากเป็นห้องสมุดสาธารณะการคำนวณอาจซับซ้อนขึ้นอยู่กับความต้องการของคุณ (ซึ่งฉันคิดว่าฉันจำได้)

หากคุณสนใจเฉพาะขนาดฮีพของกระบวนการคุณสามารถแยกวิเคราะห์[heap]รายการในmapไฟล์ได้ตลอดเวลา ขนาดเคอร์เนลที่จัดสรรไว้สำหรับฮีปกระบวนการอาจหรือไม่อาจสะท้อนถึงจำนวนไบต์ที่แน่นอนที่กระบวนการขอให้จัดสรร มีรายละเอียดนาทีเคอร์เนลภายในและออปติไมซ์ที่สามารถใช้งานได้ ในโลกอุดมคติมันจะมากเท่าที่กระบวนการของคุณต้องการจะถูกปัดเศษขึ้นเป็นหลายเท่าของขนาดหน้ากระดาษของระบบที่ใกล้ที่สุด ( getconf PAGESIZEจะบอกคุณว่ามันคืออะไร - บนพีซีมันอาจจะ 4,096 ไบต์)

หากคุณต้องการดูจำนวนหน่วยความจำที่กระบวนการจัดสรรหนึ่งในวิธีที่ดีที่สุดคือการละทิ้งเมทริกฝั่งเคอร์เนล คุณใช้เครื่องมือการจัดสรรหน่วยความจำฮีพ (de) ของไลบรารี C แทนด้วยLD_PRELOADกลไก โดยส่วนตัวฉันใช้valgrindข้อมูลในทางที่ผิดเกี่ยวกับเรื่องนี้เล็กน้อย (โปรดทราบว่าการใช้เครื่องมือจะต้องเริ่มต้นกระบวนการใหม่)

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


ขอบคุณมาก! ฉันจะตรวจสอบตัวเลือกต่าง ๆ คุณได้รับประโยชน์มากกว่า! :)
Flanfl

"คุณสามารถใช้ bash ได้ห้าร้อยอินสแตนซ์และขนาดโดยรวมของหน่วยความจำจะไม่เท่ากับผลรวมของค่า RSS หรือ VSZ" แต่ผลรวมของค่า RSS ของพวกเขาจะเป็นการประมาณที่ดีหรือไม่? เช่นเดียวกับผลรวมของคอลัมน์ที่อยู่อาศัยจาก statm ฉันไม่ต้องการค่าที่แน่นอนที่เชื่อถือได้ แต่ฉันจำเป็นต้องรู้ระดับสูงว่าหน่วยความจำกระบวนการ Java ของฉันใช้หน่วยความจำเท่าไหร่
iloveretards

3
บน Ubuntu มัน/proc/$PID/mapsผิดหรือผิดเพี้ยนไปหรือเปล่า?
dolzenko

1

ตัวอย่างที่เรียกใช้น้อยที่สุด

เพื่อความเหมาะสมคุณต้องเข้าใจพื้นฐานของการเพจ: https://stackoverflow.com/questions/18431261/how-does-x86-paging-workและโดยเฉพาะอย่างยิ่งที่ระบบปฏิบัติการสามารถจัดสรรหน่วยความจำเสมือนผ่านหน้าตาราง / การเก็บรักษาหนังสือหน่วยความจำภายใน (หน่วยความจำเสมือน VSZ) ก่อนที่จะมีหน่วยเก็บข้อมูลสำรองบน ​​RAM หรือดิสก์ (หน่วยความจำถิ่นที่อยู่ RSS)

ตอนนี้ให้สังเกตสิ่งที่เกิดขึ้นจริงกันเถอะลองสร้างโปรแกรมที่:

  • จัดสรรแรมมากกว่าหน่วยความจำกายภาพของเราด้วย mmap
  • เขียนหนึ่งไบต์บนแต่ละหน้าเพื่อให้แน่ใจว่าแต่ละหน้าเหล่านั้นไปจากหน่วยความจำเสมือนเฉพาะ RSS และ VSZ
  • ตรวจสอบการใช้หน่วยความจำของกระบวนการด้วยวิธีใดวิธีหนึ่งที่กล่าวถึงที่: https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c

main.c

#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

typedef struct {
    unsigned long size,resident,share,text,lib,data,dt;
} ProcStatm;

/* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c/7212248#7212248 */
void ProcStat_init(ProcStatm *result) {
    const char* statm_path = "/proc/self/statm";
    FILE *f = fopen(statm_path, "r");
    if(!f) {
        perror(statm_path);
        abort();
    }
    if(7 != fscanf(
        f,
        "%lu %lu %lu %lu %lu %lu %lu",
        &(result->size),
        &(result->resident),
        &(result->share),
        &(result->text),
        &(result->lib),
        &(result->data),
        &(result->dt)
    )) {
        perror(statm_path);
        abort();
    }
    fclose(f);
}

int main(int argc, char **argv) {
    ProcStatm proc_statm;
    char *base, *p;
    char system_cmd[1024];
    long page_size;
    size_t i, nbytes, print_interval, bytes_since_last_print;
    int snprintf_return;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 0x10000;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }
    if (argc < 3) {
        print_interval = 0x1000;
    } else {
        print_interval = strtoull(argv[2], NULL, 0);
    }
    page_size = sysconf(_SC_PAGESIZE);

    /* Allocate the memory. */
    base = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );
    if (base == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Write to all the allocated pages. */
    i = 0;
    p = base;
    bytes_since_last_print = 0;
    /* Produce the ps command that lists only our VSZ and RSS. */
    snprintf_return = snprintf(
        system_cmd,
        sizeof(system_cmd),
        "ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == \"%ju\") print}'",
        (uintmax_t)getpid()
    );
    assert(snprintf_return >= 0);
    assert((size_t)snprintf_return < sizeof(system_cmd));
    bytes_since_last_print = print_interval;
    do {
        /* Modify a byte in the page. */
        *p = i;
        p += page_size;
        bytes_since_last_print += page_size;
        /* Print process memory usage every print_interval bytes.
         * We count memory using a few techniques from:
         * https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c */
        if (bytes_since_last_print > print_interval) {
            bytes_since_last_print -= print_interval;
            printf("extra_memory_committed %lu KiB\n", (i * page_size) / 1024);
            ProcStat_init(&proc_statm);
            /* Check /proc/self/statm */
            printf(
                "/proc/self/statm size resident %lu %lu KiB\n",
                (proc_statm.size * page_size) / 1024,
                (proc_statm.resident * page_size) / 1024
            );
            /* Check ps. */
            puts(system_cmd);
            system(system_cmd);
            puts("");
        }
        i++;
    } while (p < base + nbytes);

    /* Cleanup. */
    munmap(base, nbytes);
    return EXIT_SUCCESS;
}

GitHub ต้นน้ำ

รวบรวมและเรียกใช้:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
sudo dmesg -c
./main.out 0x1000000000 0x200000000
echo $?
sudo dmesg

ที่อยู่:

  • 0x1000000000 == 64GiB: 2x RAM จริงของคอมพิวเตอร์ที่ 32GiB
  • 0x200000000 == 8GiB: พิมพ์หน่วยความจำทุก ๆ 8GiB ดังนั้นเราควรได้ 4 ภาพก่อนที่จะเกิดความผิดพลาดที่ประมาณ 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory: จำเป็นสำหรับ Linux เพื่อให้เราสามารถเรียก mmap ที่มีขนาดใหญ่กว่า RAM จริง: https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

เอาท์พุทโปรแกรม:

extra_memory_committed 0 KiB
/proc/self/statm size resident 67111332 768 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 1648

extra_memory_committed 8388608 KiB
/proc/self/statm size resident 67111332 8390244 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 8390256

extra_memory_committed 16777216 KiB
/proc/self/statm size resident 67111332 16778852 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 16778864

extra_memory_committed 25165824 KiB
/proc/self/statm size resident 67111332 25167460 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 25167472

Killed

สถานะการออก:

137

ซึ่งโดยกฎจำนวน 128 + สัญญาณหมายความว่าเรามีจำนวนสัญญาณ9ที่man 7 signalบอกว่าเป็นSIGKILLซึ่งจะถูกส่งโดยลินุกซ์ออกจากหน่วยความจำนักฆ่า

การตีความผลลัพธ์:

  • หน่วยความจำเสมือน VSZ ยังคงที่printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psค่าเป็น KiB) หลังจาก mmap
  • RSS "การใช้หน่วยความจำจริง" เพิ่มขึ้นอย่างเกียจคร้านเมื่อเราแตะหน้าต่างๆ ตัวอย่างเช่น:
    • ในการพิมพ์ครั้งแรกเรามีextra_memory_committed 0ซึ่งหมายความว่าเรายังไม่ได้แตะหน้าใด ๆ RSS มีขนาดเล็ก1648 KiBซึ่งได้รับการจัดสรรสำหรับการเริ่มต้นโปรแกรมปกติเช่น text area, globals เป็นต้น
    • ในการพิมพ์ครั้งที่สองเราได้เขียนถึง8388608 KiB == 8GiBมูลค่าของหน้า เป็นผลให้ RSS เพิ่มขึ้น 8GIB เป็นอย่างแน่นอน8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS เพิ่มขึ้น 8GiB ต่อไป การพิมพ์ครั้งล่าสุดแสดงหน่วยความจำประมาณ 24 GiB และก่อนที่จะพิมพ์ได้ 32 GiB นักฆ่า OOM จะฆ่ากระบวนการ

ดูเพิ่มเติมที่: ต้องการคำอธิบายเกี่ยวกับขนาดชุดของผู้อยู่อาศัย / ขนาดเสมือน

บันทึกนักฆ่า OOM

dmesgคำสั่งของเราแสดงบันทึกการทำงานของนักฆ่า OOM

การตีความที่ถูกต้องของสิ่งเหล่านั้นถูกถามที่:

บรรทัดแรกของบันทึกคือ:

[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

ดังนั้นเราจึงเห็นว่าน่าสนใจเป็น MongoDB daemon ที่ทำงานในแล็ปท็อปของฉันบนพื้นหลังซึ่งเป็นครั้งแรกที่เรียก OOM killer ขึ้นมาสันนิษฐานว่าเมื่อสิ่งที่ไม่ดีพยายามจัดสรรหน่วยความจำ

อย่างไรก็ตามนักฆ่า OOM ไม่จำเป็นต้องฆ่าคนที่ตื่นขึ้นมา

หลังจากการร้องขอเคอร์เนลจะพิมพ์ตารางหรือกระบวนการรวมถึงoom_score:

[ 7283.479292] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
[ 7283.479303] [    496]     0   496    16126        6   172032      484             0 systemd-journal
[ 7283.479306] [    505]     0   505     1309        0    45056       52             0 blkmapd
[ 7283.479309] [    513]     0   513    19757        0    57344       55             0 lvmetad
[ 7283.479312] [    516]     0   516     4681        1    61440      444         -1000 systemd-udevd

และต่อไปข้างหน้าเราจะเห็นว่าmain.outจริงๆแล้วสิ่งเล็ก ๆ น้อย ๆ ของเราถูกฆ่าตายในการร้องขอครั้งก่อน:

[ 7283.479871] Out of memory: Kill process 15665 (main.out) score 865 or sacrifice child
[ 7283.479879] Killed process 15665 (main.out) total-vm:67111332kB, anon-rss:92kB, file-rss:4kB, shmem-rss:30080832kB
[ 7283.479951] oom_reaper: reaped process 15665 (main.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:30080832kB

บันทึกนี้ระบุscore 865ว่ากระบวนการใดที่มีคะแนน OOM killer ที่สูงที่สุด (แย่ที่สุด) ตามที่กล่าวไว้ที่: OOM killer ตัดสินใจว่ากระบวนการใดที่จะต้องฆ่าก่อน

สิ่งที่น่าสนใจก็เกิดขึ้นอย่างรวดเร็วก่อนที่จะมีการบันทึกหน่วยความจำที่เป็นอิสระกระบวนการดังกล่าวoomถูกปลุกขึ้นอีกครั้งโดยDeadlineMonitor:

[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

และครั้งนี้ที่ฆ่ากระบวนการ Chromium บางอย่างซึ่งโดยปกติจะเป็นคอมพิวเตอร์หน่วยความจำปกติของฉัน:

[ 7283.481773] Out of memory: Kill process 11786 (chromium-browse) score 306 or sacrifice child
[ 7283.481833] Killed process 11786 (chromium-browse) total-vm:1813576kB, anon-rss:208804kB, file-rss:0kB, shmem-rss:8380kB
[ 7283.497847] oom_reaper: reaped process 11786 (chromium-browse), now anon-rss:0kB, file-rss:0kB, shmem-rss:8044kB

ทดสอบใน Ubuntu 19.04, Linux kernel 5.0.0

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