linux top VIRT RES SHR SWAP DATA内存参数详解

2023-10-29

总结:
VIRT 虚拟内存中含有共享库、共享内存、栈、堆,所有已申请的总内存空间。
RES  是进程正在使用的内存空间(栈、堆),申请内存后该内存段已被重新赋值。
SHR  是共享内存正在使用的空间。
SWAP 交换的是已经申请,但没有使用的空间,包括(栈、堆、共享内存)。
DATA 是进程栈、堆申请的总空间。

  其实很早之前就想开博客,写一写码农几年自己积攒下来的知识与见解。看过很多文章有过很多感触,有些收获很值得梳理一下认真思考反思的。今天就先从基本的top开始吧,在网上看了很多关于top讲解内存参数的文章,千篇一律,大部分都是不经深思的转载,自我验证的却只有少数,也许大家忙于工作,急于找到问题并快速解决,而忽略了事后思考总结给自己带来成长的乐趣了吧。不能否认生活节奏的加快让我们失去了很多自我反省与总结的时间与机会。好了,闲话到此,让我们一起看下top关于内存参数的真面目吧。
  对于使用Linux/Unix系统的人来说,top已经是在常用不过的工具了,很多人都知道里面的参数的意思,却很少有人知道这些参数值在程序中是怎么反射出来的。例如:VIRT空间为什么比物理内存大?RES的空间在程序中都包含哪几部分?SWAP交换的都是那些空间?SHR是真正开辟的共享内存空间的大小么?DATA的空间大小指的又是什么,它和RES有那些区别?通过这些内存参数怎么看出我的程序有内存泄露等等问题,待我一一给你解答。
  为了搞清楚以上的问题,我们用以下代码进行测试对其一一验证,并得出我们想要的结果。
  科普篇:先说下VIRT、RES、SHR、SWAP、DATA这几个参数的意思吧(其他参数不是本篇的重点在此略过,关于top的博客都能找到)。咱们先看下Linux下的那个无所不知的男人怎么说的:
  VIRT  -- Virtual Image (KB)  VIRT = SWAP +RES  (公式1)
  Thetotal amount of virtual memory used by the task. It includes all code, data andshared libraries plus pages that have been swapped out. (一个任务所使用的虚拟内存的总数。它包括所有的代码,数据和共享库,加上已换出的页面)说的还是比较清楚的,仔细的你或许已经从这段介绍中发现公式1是存在一些问题的(下面会做解释)
  RES  -- Resident size (KB)  RES = CODE + DATA (公式2)
  The non-swappedphysical memory a task has used. (一个任务正在使用的没有交换的物理内存)我们一般称为驻留内存空间。先给大家声明,man给出的这个公式2是有问题的,这个错误万恶的公式2估计误导了很多人。
  SHR  -- Shared Memory size (KB)
  The amount ofshared memory used by a task. It simply reflects memory that could bepotentially shared with other processes. (一个任务使用共享内存的总数。它只是反映可能与其它进程共享的内存)也就是这个进程使用共享内存的大小。
  SWAP  -- Swapped size (KB)
  Theswapped out portion of a task’s total virtual memory image. (换出一个任务的总虚拟镜像的一部分)只是说明了交换的内存来自虚拟内存,但没说明把什么样的内存交换出去。
  DATA  -- Data+Stack size (KB)
  Theamount of physical memory devoted to other than executable code, also known asthe ’data resident set’ size or DRS. (除可执行代码以外的物理内存总量,也被称为数据驻留集或DRS,谷娘了把DRS,没找到符合上下文的意思,知道的兄弟贴上来啊。)
  哦了,以上是man告诉我们的信息,至于对不对需要我们进一步确认。好了写段测试代码来验证吧。测试系统:Linux内核及系统版本2.6.23.1-42.fc8,gcc 版本 4.1.2,top: procps version 3.2.7,测试环境不同测试结果会有所不同,但得出结论的步骤都是相同的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
 
const  int BUFF_SIZE = 1 << 23;
const  int SHARED_SIZE = 1 << 24;
const  int SHM_TEST_ID = 888;
 
//4 static char GTestMem[1<<20];
//3 char GTestMem1[1<<20];
 
void TestMalloc()
{
    char   szTemp[BUFF_SIZE];
    char*  pszNew = (char*)malloc(BUFF_SIZE * sizeof(char));
    if (pszNew == NULL)
    {
        printf("Malloc memory %d failed.\n", BUFF_SIZE);
        exit(-1);
    }
 
//1    memset(szTemp, 'q', BUFF_SIZE);
//1    memset(pszNew, 'w', BUFF_SIZE);
//2    memset(szTemp, 'q', BUFF_SIZE / 4);
//2    memset(pszNew, 'w', BUFF_SIZE / 4);
    while (1)
    {
        sleep(10);
    }
}
 
void ShMemory()
{
    char   szTemp[BUFF_SIZE];
    char*  pszNew = (char*)malloc(BUFF_SIZE * sizeof(char));
    if (pszNew == NULL)
    {
        printf("Malloc memory %d failed.\n", BUFF_SIZE);
        exit(-1);
    }
 
    int  fdShMem = shmget(SHM_TEST_ID, SHARED_SIZE, 0666|IPC_CREAT);
    if (fdShMem == -1)
    {
        printf("Create shared memory failed.\n");
        exit(-1);
    }
 
    void*  pSHM = shmat(fdShMem, NULL, 0);
    if ( (int)pSHM == -1)
    {
        printf("Attach shared memory failed.\n");
        exit(-1);
    }
 
    memset(pSHM, 't', SHARED_SIZE / 4);
    while (1)
    {
        sleep(10);
    }
}
 
void MallocLeak()
{
    char   szTemp[BUFF_SIZE];
//    for (int i = 0; i < BUFF_SIZE / 4; i++)
//        szTemp[i] = i % 255;
 
    sleep(30);
    char* pszNew = NULL;
    while (1)
    {
        pszNew = (char*)malloc(BUFF_SIZE * sizeof(char));
        if (pszNew == NULL)
        {
            printf("Malloc memory %d failed.\n", BUFF_SIZE);
            exit(-1);
        }
//        memset(pszNew, 7, BUFF_SIZE / 4);
//        free(pszNew);
        sleep(5);
    }
}
 
int main(int argc, char* argv[])
{
//    TestMalloc();
    ShMemory();
    return 0;
}

测试一:使用函数TestMalloc()进行测试。
  PID     USER      VIRT  RES    SHR  S %MEM    SWAP  CODE  DATA   COMMAND
14469   root      19068    776    672  S      0.1      17m          4     16m     test_malloc     测试一
  我们先看DATA是16M可以从代码中可以看出这些空间来自于栈与堆的总和这点是可以确定的。根据man的说法VIRT = 17M + 776K怎么算也达不到19M啊,看来这个公式是不精准的,还有1M多的空间,man说来自于共享库,让我们带着问题继续寻找。RES是一个进程正在使用的空间,而SWAP的空间为什么是17M呢,代码中的16M数据来自于堆与栈,难道把这些空间全置换出去了,这样的话还多出1M的空间呢?这1M的空间和虚拟内存多出的1M空间是不是一块呢。这一轮我们只能确认DATA的空间是代码中栈与堆申请的总空间之和。
测试二:使用函数TestMalloc()并使用注释1进行测试。
 PID     USER      VIRT  RES    SHR  S %MEM    SWAP  CODE  DATA    COMMAND
15828   root      19064  16m    672   S     1.7     1916          4      16m     test_malloc     测试二
  我们看到在memset之后DATA与SHR没有发生变化,而RES的内存达到了16M,也就是说这里是堆与栈正在使用的内存,只要对该块内存进行修改,该进程正在使用内存的情况都会在这里体验出来。Memset之后SWAP的使用空间下降了,我们也可以得出结论,SWAP交换的是已经申请,但没有使用的空间,包括(栈、堆)。
测试三:验证测试二得出的结论,使用函数TestMalloc()并使用注释2进行测试。
  PID     USER      VIRT  RES   SHR  S %MEM  SWAP CODE    DATA   COMMAND
17264    root     19068  4872   672  S     0.5      13m        4        16m    test_malloc     测试三
  RES是4M,分别是使用栈与堆个2M之和,SWAP是13M是栈与堆各6M加上未知1M之和,DATA是16M。第二步得出的结论是正确的,在此我们推翻了man给出的VIRT、RES的公式结论。通过以上三步得出如下结论:
VIRT 虚拟内存中含有代码段、栈、堆,所有已申请的内存空间。
RES  是进程正在使用的内存空间(栈、堆),申请内存后该内存段已被重新赋值。
SWAP 交换的是已经申请,但没有使用的空间,包括(栈、堆)。
DATA 是进程栈+堆的总空间。
测试四:废话不多说使用ShMemory()来揭开SHR的神秘面纱
  PID     USER      VIRT  RES  SHR  S %MEM   SWAP CODE  DATA  COMMAND
18990    root     35444  4868  4772 S     0.5      29m       4      16m    test_malloc     测试四
  代码中我申请了8M栈空间,8M堆空间,16M共享内存,对共享内存前4M内存进行了赋值。我们看下以上三步时的SHR所占用的空间,672KB应该是内核管理共享内存代码段的空间,现在的SHR的值是4M,也就是正在使用的共享内存的空间,其余的12M空间应该在SWAP空间中。而这时SWAP的空间正是栈没使用的空间+堆没有使用的空间+共享内存没有使用的空间之和。多出1M的空间我确实找不到来源啊。

  Man给出的公式,在我的代码中没有得到证实,代码变化万千写法不同就会照成很多不同的结果,运行效率、占用内存各不相同。本文的目的是给大家带来思考,根据自己的知识与认知去看到表面之后的事情。让大家失望的是,我这里没能得出具体的公式结论。内存泄露大家可以使用函数MallocLeak()进行测试。
  最后遗留一个问题,测试一中SWAP多出的1M空间来源于哪?大家一起发现一起完善吧,欢迎大家的反馈。
————————————————
版权声明:本文为CSDN博主「_Vicent_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011547375/article/details/9851455

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux top VIRT RES SHR SWAP DATA内存参数详解 的相关文章

随机推荐

  • post和get的区别

    1 get是从服务器上获取数据 post是向服务器传送数据 2 get是把参数数据队列加到提交表单的ACTION属性所指的URL中 值和表单内各个字段一一对应 在URL中可以看到 post是通过HTTP post机制 将表单内各个字段与其内
  • 表驱动法(Table-Driven Methods)

    背景 表驱动方法是一种方案 它允许您在表中查找信息 而不是使用逻辑语句 if和 case 来查明信息 实际上 您可以使用逻辑语句选择的任何内容 都可以使用表进行选择 在简单情况下 逻辑语句更容易 更直接 随着逻辑链变得越来越复杂 表格变得越
  • shell中$(( )) 与 $( ) 还有${ }的区别

    http blog zol com cn 2322 article 2321763 html 与 反引号 在 bash shell 中 与 反引号 都是用来做命令替换用 command substitution 的 所谓的命令替换与我们第五
  • [改善Java代码]警惕泛型是不能协变和逆变的

    什么叫做协变 covariance 和逆变 contravariance 在变成语言的类型框架中 协变和逆变是指宽类型和窄类型在某种情况下 如参数 泛型 返回值 替换或交换的特性 简单的说 协变是用一个窄类型替换宽类型 而逆变则是用宽类型覆
  • 第三方依赖库 AG Grid调研分析

    AG Grid依赖 为实现页面表格的展示需求 进行的技术调研 官网地址 https www ag grid com GitHub地址 https github com ag grid ag grid 核心需求 1 大数据量的支持 AG Gr
  • rxjs: ReplaySubject, switchMap, merge,mergeMap

    ReplaySubject 记录 Observable 执行中的历史值并将其回放给新的订阅者 var subject new Rx ReplaySubject 3 参数 表示缓存几个历史数据 假如参数是5 也就是缓存5个值 订阅的时候可以收
  • CMake与Make

    大家都知道 写程序大体步骤为 1 用编辑器编写源代码 如 c文件 2 用编译器编译代码生成目标文件 如 o 3 用链接器连接目标代码生成可执行文件 如 exe 但如果源文件太多 一个一个编译时就会特别麻烦 于是人们想到 为什么不设计一种类似
  • 查找字符串中指定字符的第一个下标_代码的优雅写法

    刷 leetCode 有一个题目需要实现 String indexOf 的源代码 好奇之下在 Intellij IDEA 中查看了它的源代 意外地发现源码中关于在字符串中查找指定字符的下标的优雅代码 至少比我写的优雅 于是乎我打算开个贴记录
  • ASP.NET页面之间传递值的几种方式

    页面传值是学习asp net初期都会面临的一个问题 总的来说有页面传值 存储对象传值 ajax 类 model 表单等 但是一般来说 常用的较简单有QueryString Session Cookies Application Server
  • sony闪存卡/内存卡格式化修复工具

    索尼的USM formatterP工具用于格式化PhISOn主控的SONY闪存卡 修复内存卡无法格式化 使用时需要先备份所选分区上的所有数据 在格式化完成后数据将全部丢失 先选择要格式化的分区 再选择格式化的文件类型 sony闪存卡 内存卡
  • java 将天数转成月_java获取当月天数,指定年月的天数,指定日期获取对应星期(转)...

    分类 Java2012 12 25 23 52 3201人阅读 package com import java text SimpleDateFormat import java util Calendar import java util
  • 线程、多线程学习总结

    线程 多线程 1 基本概念 程序 线程 进程 1 1 程序 线程 进程 程序 一段静态代码 静态对象 进程 正在运行中的一个程序 线程 进程细化分为线程 作为调度和执行的单位 每个线程拥有独立的运行栈和程序计数器 并行 多个CPU同时执行多
  • SpringBoot从入门到精通---基础篇

    SpringBoot 一 基础篇 1 1 入手项目 注意 如果这里SpringBoot1Application 启动程序 是灰色的 你右键点击pom xml把它作为Maven项目就可以启动 BookController类中的代码 启动后访问
  • springboot @Autowired 注入为null 的原因与解决方式

    有时候我们在某个类用 Autowired 进行注入时 会发现注入参数为null 这时候会有疑惑 可能存在的原因 1 该类没有托管给spring 管理 一般在类的上面添加 Component 就可以了 2 你的这个类有被new出来的实例的 n
  • Java之BigInteger的用法

    目录 Java之BigInteger用法 一 常用方法 二 进制转换 Java之BigInteger用法 BigInteger在Java中用于操作大整数 当一个数超过了Long表示的最大范围时就可以考虑使用BigInteger来操作 Big
  • unity 设置一键重启

    以前的游戏里用过 后来查reference怎么也找不到当时用的代码了 重新看了一下工程发现是用playmaker实现的
  • [288]关于MySQL的1064错误

    MySQL的1064错误是SQL语句写的有问题时出现的 即SQL的语法错误 笔者常常使用MySQL python这个库来对MySQL进行操作 代码中报这个错误的一般是cursor execute sql param 这一行 这种参数式执行S
  • Vue的Ui框架之Mint-UI的使用方法

    基于Vue的Ui框架 饿了么公司基于vue开发的vue的Ui组件库 Element Ui 基于vue pc端的UI框架 MintUi 基于vue 移动端的ui框架 mintUI的使用 1 找官网 2 安装 npm install mint
  • stm32 freeRTOS lwip TCP快速发送,内存泄露问题

    现象1 发送缓慢 tcp write之后要等200多ms才能过发送出去 而且粘包严重 解决办法 tcp write之后 立马调用tcp output tcp就会立马发送 tcp write tcp output 现象2 持续快速发送和接受T
  • linux top VIRT RES SHR SWAP DATA内存参数详解

    总结 VIRT 虚拟内存中含有共享库 共享内存 栈 堆 所有已申请的总内存空间 RES 是进程正在使用的内存空间 栈 堆 申请内存后该内存段已被重新赋值 SHR 是共享内存正在使用的空间 SWAP 交换的是已经申请 但没有使用的空间 包括