精华内容
下载资源
问答
  • DRAM 内存介绍

    万次阅读 2018-08-14 19:24:10
    SDRAM(Synchronous dynamic random access memory),同步动态随机访问内存,通常包括 SDR (Single Data Rate) SDRAMs以及DDR (Double Data Rate) SDRAMs.在显卡中常...

    https://www.cnblogs.com/mikewolf2002/archive/2012/11/13/2768804.html

    SDRAM(Synchronous dynamic random access memory),同步动态随机访问内存,通常包括 SDR (Single Data Rate) SDRAMs以及DDR (Double Data Rate) SDRAMs.在显卡中常用的是GDDR SDRAMs以及HBM。

            如图一所示,左边就是PC系统中常用的内存条,该内存条是双通道2G内存(dual inline Memory Module),通常简称为DIMM。我们可以看到内存条上黑色的128MB内存芯片,这些内存芯片简称为IC。该内存条是双面内存,就是说正反两面都有8个IC,总共16个IC,16*128M=2GB。DIMM的单面称作rank,比如下图的2GB内存条,它就是由rank1,rank2两个单面组成,每个面有8个IC。

    图一,DRAM的组成

         每个IC内部通常由8个bank组成(DDR3通常为8个bank,GDDR5通常有16个bank),这些bank共享一个memory I/O controller, 但是在每个bank内部的读写可以并行进行。

         每个bank内部包括行地址解码器,列地址解码器,传感放大器,以及DRAM内存阵列。如图2所示,这些内存阵列由行列组成,每个行列交叉的单元,表示n bit,通常是8bit或者16位【每一位都是由一个晶体管和一个电容组成,在GDDR5和HBM内存中,通常为32Byte】,表示一个字节或者一个word。bank中的每一行组成一个page,每一行又包括很多列(这儿列是指单个交叉单元)。内存读写的最小单位就是这些交叉单元,通常只有这些单元被放入传感放大器的时候,才能够被读写,所以通常要不断在行和传感放大器之间移动数据。

        把一行放入传感放大器称作"activate”,因为这个操作会激活bank。把传感放大器的内容放入行,称作“precharge”。有时候Read或者write的时候会隐含着 precharge的操作,称作AP-read,或者AP-write,AP(auto precharge)。

       

    image

                   图二 bank内部结构

        在图一中每个bank由16k的page组成,每个page包括1k的列,每列是8bit的byte,所以总共16,384 rows/bank x 1,024 columns addresses/row x 1 byte/column address x 8 stacked banks=128M

     

        对于DDR3,我们通常说它是8n-prefetch(这儿n是指每个rank的bank数目),因为DDR3,每个IC有8个bank,每个bank读取数据的最小单位是8bit,一个byte。每次数据读取request,都会读取8*8bit=64bitdata,而不管这些数据是否都是我们所需要的,比如我们只需要其中的某个byte,但读request会读取8个byte。

         如果图三所示, SDRAM读写通常能用一个简单的状态机来描述,它的状态包括idle, active, precharging一个或多个bank。和任何其它状态机一样,从一个状态转换到另一个状态,并在新的状态开始数据操作,都需要一些最小等待时间,这些时延会影响SDRAM读写数据的性能,从而影响整个计算机系统的性能。

         SDARM bank中的内存单元行列交叉(通常称作cell  )点,用来存储数据,它通常都是一些电容和放大器组成,由于电容的特性,它的电量会随着时间衰减,比如温度等因素都会影响它的衰减速度,所以需要周期性进行加电刷新操作,维持其中的数据。刷新频率通常依赖于内存die的工艺以及cell本身的设计。对内存cell的读写和内存刷新有相同的效果,但是在电容电量衰减到必须刷新之前,并不是所有的内存cell都有读写操作,所以定时刷新仍是需要的。通常刷新操作是按行或者说page进行的,刷新之后,该行cell的电容就会被充电。通常的刷新操作周期是几百clocks到几千clocks。

         在刷新命令之前,每个bank必须要先precharged,然后处于idle状态,这需要消耗一个tRP时延(The minimum number of clock cycles required between the issuing of the precharge command and activating a different row within the same bank)。在一个刷新命令完成后,所有的bank处于precharge (idle)状态,在刷新命令和下一个activate命令(ACT)之间cycles数目必须大于等于tRFC(the Row Refresh Cycle Time )。

     

     

      

    图三,  SDRAM数据传输状态机

     

    由于数据传输时候,都有一定的时延,所以有下面的一些符号描述bank内数据传输的各个阶段时延。

    参数

    符号

    注释

    Row Active Time

    TRAS

    The minimum number of clock cycles required between a bank active command and issuing the precharge command.

    Row Address to Column Address Delay

    TRCD

    The minimum number of clock cycles required between the activation of a row and accessing columns within it.

    CAS latency

    CL

    The time between sending a column address to the memory and the beginning of the data in response. This is the time it takes to read the first bit of memory from a DRAM with the correct row already open.

    Row Precharge Time

    TRP

    The minimum number of clock cycles required between the issuing of the precharge command and activating a different row within the same bank.

    Activate to Activate in same bank.

    TRC

    The minimum number of clock cycles required between the activation of a row activting another row in the same bank.

    Burst

     

    The number of data beats in a column access. This is usually 8 for recent DDR3/GDDR5 devices.

    SDRAM在响应读写命令之前,bank必须处于激活状态,内存控制器通过发送activate命令,指定被访问的rank,bank以及page(row)。激活一个bank的时间称作tRCD,the Row-Column (or Command) Delay ,它表示激活发送active命令,program控制逻辑以及把内存行列单元读取到传感放大器中以便读写的cycles数目。

         bank激活之后,传感放大器中有完整page内容,这个时候,可以发射读写命令,指定从某列开始读写数据。从某个激活的page(放在传感放大器中)中读取一个byte数据消耗的时间称作, the Column Address Strobe (CAS) Latency ,通常间歇位CL 或者tCAS, 它包括在读写接口发送读写命令,program控制逻辑,把传感放大器的内容传输入到输入输出缓冲,并把数据的第一个word放在内存总线上总共消耗的时间。

         一个bank每次只能打开一个page(这儿打开是指把page内容放入到传感放大器),对于处于打开状态的page,我们可以进行读写操作,如果不需要再对该page进行读写操作,可以关闭该page, 把该page内容写入bank的行列单元对应的page中,以便对其它page进行读写操作。这个关闭操作通过发射一个Precharge命令实现,precharge命令可以关闭某一个bank,也可以关闭rank中所有打开的bank。

        Precharge命令可以和bank中的上一个读写操作进行绑定,从而进行一个组合操作,这时发送一个Read with Auto-Precharge (RDA) 或 Write with Auto-Precharge (WRA)代替单独的读写操作命令。只要满足一定的条件,这将允许SDRAM控制逻辑自动的打开或者关闭bank。需要满足的条件包括:(1) A minimum of RAS Activation Time (tRAS) has elapsed since the ACT command was issued, and (2) a minimum of Read to Precharge Delay (tRTP) has elapse since the most recent READ command was issued。

        precharge命令把传感放大器中的数据写入bank中对应的page中,然后DRAM core能够准备下一个数据访问。 precharge一个打开的bank所消耗的时间称作the Row Access Strobe (RAS) Precharge Delay ,通过写作tRP。同一个bank两个activate命令之间所消耗的时间称作tRC,它等于tRAS+tRP。不同bank的ACT命令间隔时间称作the Read-to-Read Delay (tRRD)。

     

    下面的时序图标出了各个阶段时延:

    image

    展开全文
  • 1、 Linux内存介绍 1.1 Linux 的虚拟内存管理有几个关键概念: 1、每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址; 2、虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址...

          查找内存泄漏问题,可以使用valgrind、malloc_stats和malloc_info 监控查看内存情况。

     

    1、 Linux内存介绍

    1.1 Linux 的虚拟内存管理有几个关键概念: 

        1、每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址; 
        2、虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址; 
        3、如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表;如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中。 

    1.2、Linux 虚拟地址空间如何分布?

        Linux 使用虚拟地址空间,大大增加了进程的寻址空间,由低地址到高地址分别为: 
        1、只读段:该部分空间只能读,不可写;(包括:代码段、rodata 段(C常量字符串和#define定义的常量) )
        2、数据段:保存全局变量、静态变量的空间; 
        3、堆 :就是平时所说的动态内存, malloc/new 大部分都来源于此。其中堆顶的位置可通过函数 brk 和 sbrk 进行动态调整。 
        4、文件映射区域 :如动态库、共享内存等映射物理空间的内存,一般是 mmap 函数所分配的虚拟地址空间。 
        5、栈:用于维护函数调用的上下文空间,一般为 8M ,可通过 ulimit –s 查看。 
        6、内核虚拟空间:用户代码不可见的内存区域,由内核管理(页表就存放在内核虚拟空间)。

     

     

    1.3 32 位系统有4G 的地址空间::

          其中 0x08048000~0xbfffffff 是用户空间,0xc0000000~0xffffffff 是内核空间,包括内核代码和数据、与进程相关的数据结构(如页表、内核栈)等。另外,%esp 执行栈顶,往低地址方向变化;brk/sbrk 函数控制堆顶_edata往高地址方向变化

    1.4 64位系统结果怎样呢? 64 位系统是否拥有 2^64 的地址空间吗? 

          事实上, 64 位系统的虚拟地址空间划分发生了改变: 
          1、地址空间大小不是2^32,也不是2^64,而一般是2^48。因为并不需要 2^64 这么大的寻址空间,过大空间只会导致资源的浪费。64位Linux一般使用48位来表示虚拟地址空间,40位表示物理地址,这可通过 /proc/cpuinfo 来查看 
    address sizes   : 40 bits physical, 48 bits virtual 

          2、其中,0x0000000000000000~0x00007fffffffffff 表示用户空间, 0xFFFF800000000000~ 0xFFFFFFFFFFFFFFFF 表示内核空间,共提供 256TB(2^48) 的寻址空间。
          这两个区间的特点是,第 47 位与 48~63 位相同,若这些位为 0 表示用户空间,否则表示内核空间。 

          3、用户空间由低地址到高地址仍然是只读段、数据段、堆、文件映射区域和栈

    2、 valgrind

          valgrind可以用来检测内存泄露,但在使用中,往往会遇到一些问题,给调试工作带来很多不必要的麻烦,我自己遇到的有以下两种:

          (1)内存泄露误检(系统初始化时,可能有一些需要长期保存在内存中的数据结构,这些空间是永远不释放的,而这些内存会被认为绝对泄露)

          (2) valgrind检查内存泄露过于全面,运行后的结果太多往往很难从中找到有用的信息。有时候,我们只需要关注某些函数,可能在执行某个操作,调用某些函数时会出现内存泄露,此时,valgrind的工作显得冗余而复杂   

    3、 mallinfo

     mallinfo函数已不推荐使用,并且都不再更新。

    可以添加代码到程序:

    #include <malloc.h>
    #include <stdio.h>
    void dumpMallinfo(void) {
      struct mallinfo m = mallinfo();
      printf("uordblks = %dnfordblks = %dn", m.uordblks, m.fordblks);
    }

    在GDB,可以 call dumpMallinfo().

    4、 malloc_stats

         系统库函数中提供了malloc_stats()函数,可以统计本进程具体的内存使用情况,精确到字节。

    glibc 提供了以下结构和接口来查看堆内内存和 mmap 的使用情况。

    struct mallinfo {
      int arena; /* non-mmapped space allocated from system */
      int ordblks; /* number of free chunks */
      int smblks; /* number of fastbin blocks */
      int hblks; /* number of mmapped regions */
      int hblkhd; /* space in mmapped regions */
      int usmblks; /* maximum total allocated space */
      int fsmblks; /* space available in freed fastbin blocks */
      int uordblks; /* total allocated space */
      int fordblks; /* total free space */
      int keepcost; /* top-most, releasable (via malloc_trim) space */
    };


    /*返回heap(main_arena)的内存使用情况,以 mallinfo 结构返回 */
    struct mallinfo mallinfo();

    /* 将heap和mmap的使用情况输出到stderr*/
    void malloc_stats();

    4.1 gdb内部调试

    call malloc_stats()

    (gdb) call malloc_stats()
    Arena 0:
    system bytes     =     135168
    in use bytes     =         96
    Total (incl. mmap):
    system bytes     =     135168
    in use bytes     =         96
    max mmap regions =          0
    max mmap bytes   =          0
    

    call malloc_info(0, stdout)

    (gdb) call malloc_info(0, stdout)
    <malloc version="1">
    <heap nr="0">
    <sizes>
    <unsorted from="1228788" to="1229476" total="3917678" count="3221220448"/>
    </sizes>
    <total type="fast" count="0" size="0"/>
    <total type="rest" count="3221220448" size="3917678"/>
    <system type="current" size="135168"/>
    <system type="max" size="135168"/>
    <aspace type="total" size="135168"/>
    <aspace type="mprotect" size="135168"/>
    </heap>
    <total type="fast" count="0" size="0"/>
    <total type="rest" count="3221220448" size="3917678"/>
    <system type="current" size="135168
    />
    <system type="max" size="135168
    />
    <aspace type="total" size="135168"/>
    <aspace type="mprotect" size="135168"/>
    </malloc>

     

    4.2 gdb外部调试

        根据 malloc_stats()的手册手册,内存信息被发送到标准错误。 一般输出到stderr 。

    命名:

    gdb --batch --pid <pid> --ex 'call malloc_stats()'

    查看stderr.log:

    ------------------------------------------------
    MALLOC:       11159496 (   10.6 MiB) Bytes in use by application
    MALLOC: +      1769472 (    1.7 MiB) Bytes in page heap freelist
    MALLOC: +      3580792 (    3.4 MiB) Bytes in central cache freelist
    MALLOC: +      1998848 (    1.9 MiB) Bytes in transfer cache freelist
    MALLOC: +     19240128 (   18.3 MiB) Bytes in thread cache freelists
    MALLOC: +      1379480 (    1.3 MiB) Bytes in malloc metadata
    MALLOC:   ------------
    MALLOC: =     39128216 (   37.3 MiB) Actual memory used (physical + swap)
    MALLOC: +            0 (    0.0 MiB) Bytes released to OS (aka unmapped)
    MALLOC:   ------------
    MALLOC: =     39128216 (   37.3 MiB) Virtual address space used
    MALLOC:
    MALLOC:            810              Spans in use
    MALLOC:            157              Thread heaps in use
    MALLOC:          32768              Tcmalloc page size
    ------------------------------------------------
    Call ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).
    Bytes released to the OS take up virtual address space but no physical memory.

    4.3 其他

    1. 如果默认编译,使用的是libc的ptmalloc内存分配器库,这个库可能有一定的内存碎片问题,我们线上也有遇到过,见comment最后
    2. 任然无法查出,得上通用的内存分配扫描工具 。比如systap脚本抓取malloc/free的栈记录,然后分析记录信息查看。比如valgrind --mem-leak(往上可以搜索方法, 注意)

    4.3.1 glibc库的ptmalloc2信息采集:

    强制nginx worker调用malloc_stats()函数,让其将数据输出到stderr(nginx重定向到error.log)
    --------
    $ gdb -p $(pgrep -P $(cat logs/nginx.pid)) -ex 'call malloc_stats()'
    
    error.log内dump出来的信息
    -----
    Arena 0:                     <<< 线程 0(一般tengine worker内只有1个thread)
    system bytes     =    1372160     <<< 从os抽取的内存(一般为mmap)        
    in use bytes     =    1198448     <<< 应用程序malloc的
    Total (incl. mmap):
    system bytes     =    1372160
    in use bytes     =    1198448
    max mmap regions =          6
    max mmap bytes   =    2789376
    

    4.3.2 for jemalloc:

    $ gdb -batch -p <pid> -ex 'call malloc_stats_print(0,0,0)'

          如果想知道堆内片究竟有多碎 ,可通过 mallinfo 结构中的 fsmblks 、 smblks 、 ordblks值得到,这些值表示不同大小区间的碎片总个数,这些区间分别是 0~80 字节, 80~512 字节,512~128k 。如果 fsmblks 、 smblks 的值过大,那碎片问题可能比较严重了。

          不过, mallinfo 结构有一个很致命的问题,就是其成员定义全部都是 int ,在 64 位环境中,其结构中的 uordblks/fordblks/arena/usmblks 很容易就会导致溢出,应该是历史遗留问题,使用时要注意

    5、 除了 glibc 的 malloc/free ,还有其他第三方实现吗?

          其实,很多人开始诟病 glibc 内存管理的实现,就是在高并发性能低下和内存碎片化问题都比较严重,因此,陆续出现一些第三方工具来替换 glibc 的实现,最著名的当属 google 的tcmalloc 和 facebook 的 jemalloc 。网上有很多资源,可搜索之,这里就不详述了。

     

    6、 既然堆内内存brk和sbrk不能直接释放,为什么不全部使用 mmap 来分配,munmap直接释放呢? 

            既然堆内碎片不能直接释放,导致疑似“内存泄露”问题,为什么 malloc 不全部使用 mmap 来实现呢(mmap分配的内存可以会通过 munmap 进行 free ,实现真正释放)?而是仅仅对于大于 128k 的大块内存才使用 mmap ? 

            其实,进程向 OS 申请和释放地址空间的接口 sbrk/mmap/munmap 都是系统调用,频繁调用系统调用都比较消耗系统资源的。并且, mmap 申请的内存被 munmap 后,重新申请会产生更多的缺页中断。例如使用 mmap 分配 1M 空间,第一次调用产生了大量缺页中断 (1M/4K 次 ) ,当munmap 后再次分配 1M 空间,会再次产生大量缺页中断。缺页中断是内核行为,会导致内核态CPU消耗较大另外,如果使用 mmap 分配小内存,会导致地址空间的分片更多,内核的管理负担更大
            同时堆是一个连续空间,并且堆内碎片由于没有归还 OS ,如果可重用碎片,再次访问该内存很可能不需产生任何系统调用和缺页中断,这将大大降低 CPU 的消耗。 因此, glibc 的 malloc 实现中,充分考虑了 sbrk 和 mmap 行为上的差异及优缺点,默认分配大块内存 (128k) 才使用 mmap 获得地址空间,也可通过 mallopt(M_MMAP_THRESHOLD, <SIZE>) 来修改这个临界值。

    malloc系统有自己的内存池管理策略:

          1、malloc的时候,检测池中是否有足够内存,有则直接分配,无则从内存中调用brk/mmap函数分配,一般小于等于128k(可设置)的内存,使用brk函数,此时堆向上(有人有的硬件或系统向下)增长,大于128k的内存使用mmap函数申请,此时堆的位置任意,无固定增长方向。

         2、free的时候,检测标记是否是mmap申请,是则调用unmmap归还给操作系统,非则检测堆顶是否有大于128k的空间,有则通过brk归还给操作系统,无则标记未使用,仍在glibc的管理下。

          glibc为申请的内存存储多余的结构用于管理,因此即使是malloc(0),也会申请出内存(一般16字节,依赖于malloc的实现方式),在应用程序层面,malloc(0)申请出的内存大小是0,因为malloc返回的时候在实际的内存地址上加了16个字节偏移,而c99标准则规定malloc(0)的返回行为未定义。除了内存块头域,malloc系统还有红黑树结构保存内存块信息,不同的实现又有不同的分配策略。频繁直接调用malloc,会增加内存碎片,增加和内核态交互的可能性,降低系统性能。

     

    7、如何查看进程的缺页中断信息? 

          可通过以下命令查看缺页中断信息 

    ps -o majflt,minflt -C <program_name> 
    ps -o majflt,minflt -p <pid> 
    MAJFLT MINFLT
         0  27698

         其中:: majflt 代表 major fault ,指大错误;    minflt 代表 minor fault ,指小错误。

        这两个数值表示一个进程自启动以来所发生的缺页中断的次数。
        其中 majflt 与 minflt 的不同是:

            majflt 表示需要读写磁盘,可能是内存对应页面在磁盘中需要load 到物理内存中,也可能是此时物理内存不足,需要淘汰部分物理页面至磁盘中。

    参考:

    https://github.com/alibaba/tengine/issues/1043

    https://blog.csdn.net/origin_lee/article/details/42740535

    http://landcareweb.com/questions/20566/jian-cha-gdbzhong-de-c-c-dui-nei-cun-tong-ji-xin-xi

    展开全文
  • 操作系统内存介绍

    千次阅读 2015-09-24 14:22:07
    内存的分类:物理内存、虚拟内存、共享内存,它们分别对应top命令输出中的RES、VIRT、SHR三列。 物理内存 系统的物理内存被划分为许多相同大小的部分,也称作内存页。 内存页的大小取决于CPU的架构和操作系统的...

    前言

    • 内存的分类:物理内存、虚拟内存、共享内存,它们分别对应top命令输出中的RES、VIRT、SHR三列。

    物理内存

    • 系统的物理内存被划分为许多相同大小的部分,也称作内存页
    • 内存页的大小取决于CPU的架构和操作系统的配置,一般为4KB。
    • 物理内存的使用主要分为以下几方面:
    • 内核使用:

      • 操作系统启动时,位于/boot目录下的压缩内核文件会被加载到内存中并解压。这部分内容在系统允许期间都会常驻在内存的起始位置。

    • 进程使用 :

      • 驻留内存:进程实际使用的物理内存(包含共享库占用的内存)称为驻留内存,简称RSS(Resident Set Size)。

    • 页缓存(page cache)

      • 概念:磁盘io的速度远远低于内存的访问速度,为了加快访问磁盘数据的速度,操作系统使用部分物理内存作为缓存,读写缓存的单位是内存页,故我们把这个缓存称为页缓存(page cache)。页缓存会尽可能的保存着从磁盘读入的数据(现代的操作系统几乎所有可用的物理内存都作为页缓存)。

        • 当一个进程准备读取磁盘上的文件内容时,操作系统会先查看待读取的数据所在的页(page)是否在页缓存(pagecache)中,如果存在(命中)则直接返回数据,从而避免了对物理磁盘的 I/O 操作;如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入页缓存,之后再将数据返回给进程。

        • 当一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后将数据写入对应的页。被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。

        • Linux 操作系统中的 vm.dirty_background_ratio 参数用来指定当脏页数量达到系统内存的百分之多少之后就会触发 pdflush/flush/kdmflush 等后台回写进程的运行来处理脏页,一般设置为小于10的值即可,但不建议设置为0。与这个参数对应的还有一个 vm.dirty_ratio 参数,它用来指定当脏页数量达到系统内存的百分之多少之后就不得不开始对脏页进行处理,在此过程中,新的 I/O 请求会被阻挡直至所有脏页被冲刷到磁盘中。

        • 页缓存占用的内存随时可以释放掉,以便及时腾出更多的物理内存供用户进程使用。

      • 优势:用户进程重启,页缓存还是会保持有效,维护页缓存和文件之间的一致性交由操作系统来负责,这样会比进程内维护更加安全有效,同时也极大地简化了代码逻辑。

      • 页缓存中的块缓存(Buffer Cache)

        • 磁盘的最小数据单位为扇区(sector),每次读写磁盘都是以sector为单位对磁盘进行操作。sector大小跟具体的磁盘类型有关,有的为512Byte, 有的为4K Bytes。

        • 为了减少直接读取磁盘的低效访问,尽可能的提升磁盘访问性能,操作系统使用部分物理内存作为缓存,缓存部分sector数据在内存中,读写缓存的单位是块block(sector的整数倍),我们把这个缓存称为块缓存(buffer cache)。

          • 当有数据读取请求时,他能够直接从缓存中将对应数据读出。

          • 当有数据写入时,他可以直接再内存中直接更新指定部分的数据,然后再通过异步方式,把更新后的数据写回到对应磁盘的sector中。这层缓存则是块缓存Buffer Cache。

        • 一个页缓存(page cache)包含多个块缓存(buffer cache),页缓存中的存储的数据实际上是由多个块缓存来负责存储。

        • File在地址空间上,以4K(page size)为单位进行切分,每一个4k都可能对应到一个page上(只有被缓存的部分,才会对应到page上,没有缓存的部分,则不会对应),而这个4k的page,就是这个文件的一个Page Cache。而对于落磁盘的一个文件而言,最终,这个4k的page cache,还需要映射到一组磁盘block对应的buffer cache上,假设block为1k,那么每个page cache将对应一组(4个)buffer cache,而每一个buffer cache,则有一个对应的buffer cache与device block映射关系的描述符:buffer_head,这个描述符记录了这个buffer cache对应的block在磁盘上的具体位置。

        • 页缓存的大小是在一直动态变化的。当系统内存充足时,页缓存会一直增大;当系统free内存不足时,这时如果有进程申请内存,操作系统会从page cache中回收内存页进行分配,如果page cache也已不足,那么系统会将当期驻留在内存中的数据置换到事先配置在磁盘上的swap空间中,然后空出来的这部分内存就可以用来分配了。这就是swap交换。

        • 出现swap交换时,数据被置换到swap空间后(swap out),该进程使用的内存量下降,但这并不表示该进程释放了内存,当它需要时,这部分数据又会被换入到内存中(swap in)。另外, swap交换往往会带来磁盘IO的大量消耗,严重影响到系统正常的磁盘io。出现大量的swap交换说明系统已经快要不行了,需要重点关注。

     

    虚拟内存

    • 通过引入虚拟内存,每个进程都有自己独立的虚拟地址空间,这个空间理论上可以无限大,因为它并不要钱。一个进程同一时刻不可能所有变量数据都会访问到,只需要在访问某部分数据时,把这一块虚拟内存映射到物理内存,其他没有实际访问过的虚拟地址空间并不会占用到物理内存,这样对物理内存的消耗就大大减少了 。

    • 系统内核为每个进程都维护了一份从虚拟内存到物理内存的映射表,称为页表

    • 页表根据虚拟地址,查找出所映射的物理页位置和数据在物理页中的偏移量,便得到了实际需要访问的物理地址。

    • 虚拟内存的大小 = 物理内存大小 + 交换空间的大小。top命令中:VIRT=SWAP+RES:

    • 驻留内存(RSS):指虚拟内存中实际映射到物理内存的那部分,也就是进程实际占用的物理内存大小。判断一个进程使用内存的大小,我们主要是看这个进程的驻留内存大小。

     

    共享内存

    • 进程在运行过程中,会加载许多操作系统的动态库,比如 libc.so、libld.so等。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
    • 注意,进程占用的共享内存也是计算到驻留内存中的。

     

    展开全文
  • c与c++的内存管理进行介绍。Java在需要对象的时候直接new一个出来就好了,使用完之后一般不需要考虑内存释放的问题,GC会负责垃圾回收的善后工作,但是有时候它的回收工作做的并不是万无一失。在C语言中,想使用内存...

    本文主要分为三个部分。第一部分是对linux的内存做一下简单介绍。第二部分是对C和C++内存管理的几个关键字做一些介绍和它们的不同。第三部分是参考资料。

    • 第一部分

    这一部分包含有两部分内容。第一部分是在Linux中内存的分配;第二部分是内存的管理。

    内存分配:

    在linux中对内存的申请是做如下操作。当申请成功的时候,只是返回的逻辑地址,即从某个地址到某个地址区间的内存你可以使用。这个时候物理内存是没有分配,只有你做真实的写操作的时候,真正的物理内存才分配下来。

    而且当申请内存的时候,一般会分配比申请内存要大的一块内存区域,把这块内存缓冲下来。这样下次申请的的时候就可以在缓存内首先查找,而不需要去操作系统申请。同样在内存释放的时候,也不会立即把内存释放给操作系统,也是缓冲下来。这也可以解释为何有些时候申请内存的时候,内存里会有乱七八糟的数据。这就是因为少一次申请内存中的数据么有被清零,而这块内存又被缓冲下来。

    对于linux中内存分配如下图所示:



    其中在可以看出对于32位机器,可以用4G内存可用。其中1G内存是操作系统保留,我们可用内存是3G内存。对于内存主要分为

    代码段:是从0地址开始,主要存放编译好的代码。

    数据段:代码段上面就是数据段,存放程序中已经初始化的数据,包括静态变量,final变量等一些全局变量。它是属于静态内存分配,是不参与程序运行期的动态分配。

    BSS(Block Started by Symbo):存放声明但是没有初始化的全局变量。

    堆与栈。我们程序中经常提到,或者我们可以直接使用的主要是两部分stack与heap,即我们平时提到的栈内存与堆内存。请java程序员不要把这里的栈内存与堆内存和java中的内存管理中的名词相混淆。栈内存是从高地址向低地址增长,堆内存是从低地址向高地址增长。

    而对于堆与栈之间的mmap内存区域,它其实和堆栈是差不多的,也是程序运行期动态分配内存。它们两个不同的是,当申请对象不同的时候,分配到不同的区域。当对象是个小对象生命周期比较短的时候,则在堆内存分配;对象比较大而且生命周期比较长,那么就在mmap区域分配。一般通过系统调用mmap()方法申请内存,可以把一个文件映射到内存中,通过访问内存的方式访问文件。

    另外,很多申请内存的方式底层一般都是调用的mmap()系统调用。

    内存管理:

    对于内存的管理,Linux是通过虚拟地址映射让进程可以不由考虑内存的限制,通过进程的换入和换出来实现内存的共享(请查看内存swap介绍),虚拟地址到物理地址的映射通过段页机制进行管理。每个进程或者作业拥有一张段表,每一个段拥有一个页表用来把段内地址通过页表映射到物理地址空间。

    • 第二部分:

    C语言不同于Java。Java在需要对象的时候直接new一个出来就好了,使用完之后一般不需要考虑内存释放的问题,GC会负责垃圾回收的善后工作。它的回收机制也有很多种,可以参考底部的参考资料。但是它的善后工作做的并不是万无一失,内存回收失败的场景会有很多种,这个不是我们这篇文章来考虑的了。在C语言中,想使用内存必须自己去申请,然后在使用结束之后再把内存手动回收,否则这些内存就会导致内存泄漏。

    在C语言中内存申请有三种方式,不同方式有不同的应用场景。下面就主要说一下不同方法之间的不同。

    1) malloc 函数: void *malloc(unsigned int size)

         在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。malloc在底层会根据具体情况进行系统调用brk或者mmap

    2)calloc 函数: void *calloc(unsigned int num, unsigned int size)

         按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。

     3)realloc 函数: void *realloc(void *ptr, unsigned int size)

         给一个原有的指针ptr动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。申请的内存空间不会进行初始化。realloc会释放传进去的内存,返回新分配的内存。

        另外,当申请的size比原来的小的时候将返回空指针。当申请的比原来大的时候,分为两种情况

    下面给出代码运行效果

            char *p = (char *) malloc(SIZE * sizeof(char));
    	printf("%s\n", p);	
    	p = (char *) calloc(1, SIZE * sizeof(char));
    	printf("%s\n", p);
    	p = "a";
    	printf("%s\n", p);
    

    输出结果:**(乱码)空行 a。另外,在分配内存之后,记得释放资源

    在这里还想在着重说一下realloc的其他需要注意的地方。第一点:一般realloc是用来扩大原来的内存大小,所以第二个参数size大小一般要大于原来大小,否则会因为编译器不同而出现分配失败或者报错现象;第二点:第一个参数指针可以为null,这时候realloc就和malloc使用一样,但是分配的内存是初始化了的;第三点:该方法扩大内存的时候,首先会在原有内存底部开始扩展,如果底部内存满足要求,则直接向下扩展,这时候新返回的内粗地址与原有的内粗起始地址相同,如果底部内存不满足要求,则把原有内存的数据一同拷贝到新的位置,这时候新返回的指针地址和原有地址就不同了,如果没有满足要求的内存,那么就返回null了,对原有指针不会产生影响。

    对于C++语言中,不仅你可以使用malloc,还有new可以申请内存。那么它们之间有什么差别那。

    对于malloc是属于标准函数,其申请的内存是没有任何属性的,所以对申请完的内存要进行强制类型转换为你需要的内存类型。new则是C++特有的运算符。malloc无法满足动态管理对象的要求,而C++又是主要面向对象操作的。在对象在生成要自动执行构造函数,对象在消亡之前要自动执行析构函数。对于new操作,其完成了两部分工作,第一部分申请内存,第二部分调用构造函数进行该内存的初始化。所以如果仅使用malloc的话,是无法完成对内存的初始化操作的。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free,所以C++创建了new/delete运算符,用来实现创建对象调用构造,消亡对象调用析构。


    参考资料:

    动态内存分配:wiki动态内存分配

    存储管理的习题:http://ettc.sysu.edu.cn/2005wlkc/caozuoxitong/Practice/homework/homework6.HTM

    垃圾回收:垃圾回收

    展开全文
  • 虚拟内存介绍及MMU工作原理(一)

    千次阅读 2012-03-22 20:35:54
    在linux,包括很多操作系统中,虚拟内存的概念十分重要。在使用windows的过程中,我们经常会遇到“虚拟内存不足”的提示,linux中有个所谓的swap分区,包括linux和uClinux的最大区别,就是有没有MMU。那么,究竟什么...
  • C++ 内存介绍与经典内存池的实现

    万次阅读 多人点赞 2015-11-01 00:04:38
    1.默认内存管理函数的不足利用默认的内存管理函数new/delete或malloc/free在堆上分配和释放内存会有一些额外的开销。系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定的...
  • JVM内存区域介绍

    千次阅读 2018-02-08 10:28:22
    写博客的好处是,学过的知识及时记录下来,过段时间可以用来复习。...其中除了堆和方法区是线程共有的,其他几个区域都是线程私有的,下面分别介绍: 1、程序计数器 程序计数器是一块较小的内存,通过这个计数...
  • 常用内存数据库介绍

    万次阅读 2018-12-29 21:33:00
    1.内存数据库简介1.概念2.内存数据库历史和发展3.常用内存数据库 1.SQLite1.SQLite最佳试用场合2.哪些场合适合使用其他的关系型数据库管理系统RDBMS2.Altibase3.Oracle 内存数据库系列 Berkeley DB 和 TimesTen1....
  • C语言内存模型详细介绍_堆栈介绍

    千次阅读 多人点赞 2018-05-22 18:31:28
    (命令行参数区其实就是在通过dos或shell脚本调用时传递的参数,比如:a.exe 123 123)上图是C语言内存模型,其实虽然说叫C语言内存模型,其实并不是叫C语言内存模型,而是C语言根据CPU处理器搭建出来的一个模型!...
  • memtest86内存测试工具介绍

    万次阅读 2017-10-10 23:19:31
    memtest86内存测试工具介绍
  • 内存管理算法介绍

    千次阅读 2015-01-09 18:42:06
    前面两小节介绍了硬件支持的内存管理机制,尤其是如何将虚拟地址或者逻辑地址转译成物理内存地址。这一节我们将首先讨论在一个地址空间内部如何有效地进行动态内存管理,然后介绍常用的页面替换算法,以及在进程内存...
  • 内存复用介绍

    2015-04-01 15:34:34
    内存复用是指在服务器物理内存一定的情况下,通过综合运用内存复用单项技术(内存气泡、内存共享、内存交换)对内存进行分时复用。通过内存复用,使得虚拟机内存规格总和大于服务器规格内存总和,提高服务器中虚拟机...
  • JAVA直接内存(堆外内存

    千次阅读 2019-03-25 13:57:05
    堆外内存介绍
  • optee的共享内存介绍

    千次阅读 2020-08-20 20:10:24
    share memory是一块内存区域, 用于non-secure world和secure world的通信. optee支持两种share memory: 连续的buffer,非连续的buffer optee是按照buffer来管理共享内存的,而不是按照pool来管理的. 每一个buf需要...
  • SAP内存和ABAP内存的简单介绍说明

    千次阅读 2017-08-28 15:24:32
    前者的值是放在SAP内存,后者是放在ABAP内存。这两种内存的主要的区别就是作用范围了,SAP内存是对当前登录所有会话都可以访问到的,除非用户退出系统不然在整个登录期间都有效。ABAP内存只是针对一个会话,你用哪个...
  • 海思MPP业务MMZ内存优化介绍

    万次阅读 2017-12-28 10:24:45
    海思MPP业务MMZ内存优化介绍 概述  完全是本着内存吃紧的情况下的一些优化点,都是在文档有介绍的,自己多调试下就出来了,好不好用,还请自己评估,这里只是给出一些点的介绍。本着有坑也不填的原则,这里只做交流...
  • QEMU中的内存管理介绍

    千次阅读 2016-05-08 12:36:50
    @(qemu源码阅读与分析)QEMU中的内存管理(介绍)QEMU在虚拟机启动的初始化阶段,为客户机分配了物理内存,那么客户机的物理内存如何工作呢?本篇文档,为大家介绍客户机物理内存的工作原理、相关数据结构,但不会涉及...
  • Python内存管理算法介绍

    千次阅读 2014-04-08 18:53:10
    介绍 使用Python语言的一个好处是Python和其它一些高级语言一样,会进行自动的内存管理。它使用引用计数机制检测为对象分配的内存是否可以被释放。然而,在Python中内存永远不会还给操作系统,Python会持有这些...
  • 史上最清晰的Java内存模型介绍

    万次阅读 多人点赞 2018-07-09 19:45:22
    网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说自己更懵了。本文,就来整体的介绍一下...
  • 内存池技术介绍

    万次阅读 2009-02-23 13:51:00
    看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此。 原贴地址:http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html 6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的...
  • JVM五大内存区域介绍

    千次阅读 2019-07-11 11:40:21
    程序计数器是一块很小的内存空间,它是线程私有的,可以认作为当前线程的行号指示器。 那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法】,那么计数器为空。 这块内存区域是虚拟机规范中唯一没有...
  •  free命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。  total:去掉为硬件和操作系统保留的内存后剩余的内存总量。许多人奇怪自己的电脑安装了一共8G的内存,但是显示总共...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,123,551
精华内容 449,420
关键字:

内存介绍