如何在C++/g++中获取可用内存?

86

我希望按照可用内存来分配缓冲区。这样,当我进行处理时,内存使用量会增加,但仍然保持在可用内存限制范围内。是否有一种方法可以获取可用内存(我不知道虚拟内存或物理内存状态是否会有所不同?)该方法必须是平台无关的,因为它将在Windows、OS X、Linux和AIX上使用。(如果可能的话,我还想分配一些可用内存给我的应用程序,以便在执行期间不发生更改。)

编辑:我已经使用可配置的内存分配方式来解决了这个问题。我知道这不是一个好主意,因为大多数操作系统都会为我们管理内存,但我的应用程序是一个ETL框架(旨在在服务器上使用,但也作为Adobe Indesign插件在桌面上使用)。因此,我遇到了问题,因为Windows没有使用交换而返回错误代码,并导致其他应用程序开始失败。因此,由于我被教导要避免崩溃,因此只是试图优雅地降级。


13
做这件事情没有意义。在所有现代操作系统上,一个应用程序所使用的内存并不会影响其他应用程序可用的内存,因为它们都是虚拟的。只分配你需要的内存即可。 - Martin York
9
当然是假的。系统只能分配有限的资源。我选择不使用交换文件,因此我的系统有8GiB内存,超过这个限制后,C++中的new操作将抛出bad_alloc异常,其他应用程序也会失败。在Linux和最近的Windows系统中,有一个OOM Killer(内存耗尽杀手)会选择关闭某些应用程序。病毒可以在多个进程中分配大量资源,并利用这个事实来崩溃其他应用程序。更不用说,如果你有一个页面文件,系统将变得缓慢且无法使用(通常只会死机,但在Windows系统中没有Ctrl-Alt-F1)。 - v.oddou
1
@v.oddou:这些都与问题的上下文无关,因此我的评论仍然有效。 - Martin York
2
@v.oddou Linux OOM killer 会很快杀死虚构的病毒;低运行时间,低 CPU 使用率,高内存使用率,许多子进程。这个无用的病毒基本上是在它自己和它的子进程身上画了一个大红十字。 - yyny
2
@Loki Astari 并非每个人的目的都相同,编写一个垃圾收集器,在内存不足时更频繁地进行收集是非常有用的。 - yyny
显示剩余8条评论
10个回答

174

在类UNIX操作系统中,存在sysconf函数。

#include <unistd.h>

unsigned long long getTotalSystemMemory()
{
    long pages = sysconf(_SC_PHYS_PAGES);
    long page_size = sysconf(_SC_PAGE_SIZE);
    return pages * page_size;
}

在Windows系统中,有一个名为GlobalMemoryStatusEx的函数:

#include <windows.h>

unsigned long long getTotalSystemMemory()
{
    MEMORYSTATUSEX status;
    status.dwLength = sizeof(status);
    GlobalMemoryStatusEx(&status);
    return status.ullTotalPhys;
}

所以只需进行一些花哨的#ifdef操作,你就可以开始了。


13
并不是说我想要用尽所有的内存,而是我不想去加载太多数据,以至于在现有的内存中无法处理(我希望留出未使用或某些空间,这些空间可能不会被其他进程访问)。再次强调,我不想愚蠢地想要分配所有可用内存,而是想决定应用程序的限制,以免它吸取所有的内存并崩溃。___ - theCakeCoder
3
这对我有用,因为我需要完成一个轻微相关的任务:当用户使用了相当大比例的物理内存时警告他们。虽然我知道可以使用更多的虚拟内存来管理,但如果我使用的内存超过了物理RAM的数量,我希望能够警告用户,因为这将导致页面置换,从而降低速度。 - Chris Westin
5
小细节:status.ullTotalPhys 是一个 unsigned long long 类型; 如果这个方法的返回类型是 long,在某些系统上会得到无意义的结果。在我的系统上运行原始代码会得到一个 -729088 的返回值,但将其改为匹配 ullTotalPhys 类型的类型后,结果将正确为 21474107392 - Showtime
2
这是一个很好的观点...我在代码中将long更改为size_t,这在任何具有非分段寻址的系统上都应该没问题。 - Travis Gockel
5
值得注意的是,"_SC_PHYS_PAGES" 不是 POSIX 规范的一部分。 - Craig Barnes
显示剩余9条评论

35

在科学软件领域中,有理由这样做。(不是游戏,网络,商业或嵌入式软件)。科学软件通常需要处理数千兆字节的数据才能进行一次计算(或运行)(并且需要运行几个小时或几周) - 所有这些都无法存储在内存中(如果有一天你告诉我一千兆字节是任何PC或平板电脑或手机的标准,那么科学软件将被期望处理拍字节或更多)。内存的数量也可能决定适合的方法/算法类型。用户并不总是想要决定内存和方法 - 他/她还有其他事情要担心。因此,程序员应该对可用的内存(如4Gb、8Gb或64Gb等)有一个很好的了解,以确定是否可以自动使用某种方法或需要选择一种更费力的方法。使用磁盘,但内存更可取。当运行此类软件时,不鼓励用户在计算机上执行太多操作 - 实际上,他们经常使用专用的机器/服务器。


这不完全是科学软件,更像是我正在构建一个ETL框架,当然它旨在运行在专用服务器上。可能需要设置最大允许内存,就像Java或Maltab一样作为启动参数。 - theCakeCoder
2
至少使用渲染软件有一些原因。您希望尽可能地利用内存。例如:可用的物理内存(×α,其中0.5 <α <0.8)将是光子映射大小的限制。+一些 min(physi, 2GiB),以避免具有256GB RAM的机器永远构建光子映射。但仍然。您还可以想象在游戏中漫游,我已经看到引擎流入和流出资产以维护内存目标。您拥有的内存越多,您可以看得越远。 - v.oddou

16

目前没有一种跨平台的方法可以做到这一点,不同的操作系统使用不同的内存管理策略。

以下的其他stackoverflow问题可能会有所帮助:

但你需要注意:在linux中获取“实际”可用内存是非常困难的。操作系统显示的进程使用的内存并不能保证实际为进程分配的内存。

这是开发嵌入式linux系统(如路由器)时经常遇到的问题,因为你希望缓存尽可能多的数据以达到硬件允许的极限。这里有一个示例链接,展示了如何在linux中获取此类信息(使用C语言):


Linux使用惰性分配,您可以通过在其中写入来保证内存已分配。 - v.oddou

13

看完这些答案后,我很惊讶,有这么多人认为OP的计算机内存属于其他人。这是他的计算机和他的内存,他可以按照自己的意愿使用,即使这样会破坏其他系统的权利。这是一个有趣的问题。在一个更原始的系统上,我有memavail()函数来告诉我这一点。为什么 OP 不能取得他想要的内存而不会干扰其他系统呢?

以下是一个分配少于可用内存一半的解决方案,只是为了友善起见。输出结果为:

所需内存 FFFFFFFF

所需内存 7FFFFFFF

所需内存 3FFFFFFF

已分配内存大小 = 1FFFFFFF

#include <stdio.h>
#include <stdlib.h>

#define MINREQ      0xFFF   // arbitrary minimum

int main(void)
{
    unsigned int required = (unsigned int)-1; // adapt to native uint
    char *mem = NULL; 
    while (mem == NULL) {
        printf ("Required %X\n", required);
        mem = malloc (required);
        if ((required >>= 1) < MINREQ) {
            if (mem) free (mem);
            printf ("Cannot allocate enough memory\n");
            return (1);
        }
    }

    free (mem);
    mem = malloc (required);
    if (mem == NULL) {
        printf ("Cannot enough allocate memory\n");
        return (1);
    }
    printf ("Memory size allocated = %X\n", required);
    free (mem);
    return 0;
}

2
在Linux中,您可以使用binutils的“free”命令(或者是bash命令?也许)使用execvesystem启动。一个有趣的方法也可以尝试分配(并写入1)直到失败以检测可用内存。更不用说检查速度以便检测交换。 - v.oddou
40
这是一个极其糟糕的解决方案。想象一下,当你正在使用电脑时,突然它开始交换并且变得非常缓慢,一些应用程序因为内存不足而失败,网络连接也中断了等等。你会惊慌失措地认为自己感染了恶意软件,于是关机或运行杀毒软件,结果发现这全都是由某个愚蠢的应用程序不断地分配和释放大量根本不需要的内存所导致。 - Michael
4
尝试使用malloc()并确定可用内存是解决该问题的可怕方法;远非最佳和可用的方案。为什么不使用sysctl()函数族并从操作系统调整参数中获取一些读数呢?此外,“可用内存”的概念因操作系统而异,例如,FreeBSD和我所知道的OS X都将未使用的内存视为浪费,并将该内存用于一些“有用的东西”(这个问题的答案超出了本主题的范围)。请查看 https://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3 - fnisi
1
@FehmiNoyan 我同意这不是很优雅,但Windows API没有任何sysctl函数族(如果我错了请纠正我)。在旧时代的Borland Turbo C中有memavail函数,但MSVC似乎没有相应的函数。在另一个受欢迎的答案中,建议使用GlobalMemoryStatusEx函数,但是使用MSVC编译的程序只允许程序使用约2Gb的内存。我的系统有8Gb内存,那么这会影响其他程序吗?但是如果我需要它,而其他应用程序阻止我按照自己的意愿进行操作,在我的个人电脑上,我会关闭它们。 - Weather Vane
1
使用虚拟内存,可以分配比实际物理内存更多的虚拟内存。malloc() 可能会告诉你有那么多内存,但只有在你真正使用该内存时(触发页面故障异常,由操作系统分配一个空闲页;可能通过置换某些内容或者倾倒高速缓存来释放页),每个虚拟页才会被分配到一个物理页。因此,如果您尝试使用您分配的所有内存,则可能会导致系统过载,甚至被操作系统杀死或导致其他进程被杀死。 - jtchitty
显示剩余6条评论

9

以下是使用 sysctl (man 3 sysctl)的 Mac OS X 示例:

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/sysctl.h>

int main(void)
{
    int mib[2] = { CTL_HW, HW_MEMSIZE };
    u_int namelen = sizeof(mib) / sizeof(mib[0]);
    uint64_t size;
    size_t len = sizeof(size);

    if (sysctl(mib, namelen, &size, &len, NULL, 0) < 0)
    {
        perror("sysctl");
    }
    else
    {
        printf("HW.HW_MEMSIZE = %llu bytes\n", size);
    }
    return 0;
}

(也可能适用于其他类似BSD的操作系统?)

6
这会返回系统中的总物理内存,而不是可用(空闲)内存。 - Violet Giraffe

5
下面的代码可以给出以兆字节为单位的总内存和可用内存。适用于FreeBSD,但您应该能够在您的平台上使用相同/类似的sysctl可调参数并执行相同的操作(Linux和OS X至少有sysctl)。
#include <stdio.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>

int main(){
    int rc;
    u_int page_size;
    struct vmtotal vmt;
    size_t vmt_size, uint_size; 

    vmt_size = sizeof(vmt);
    uint_size = sizeof(page_size);

    rc = sysctlbyname("vm.vmtotal", &vmt, &vmt_size, NULL, 0);
    if (rc < 0){
       perror("sysctlbyname");
       return 1;
    }

    rc = sysctlbyname("vm.stats.vm.v_page_size", &page_size, &uint_size, NULL, 0);
    if (rc < 0){
       perror("sysctlbyname");
       return 1;
    }

    printf("Free memory       : %ld\n", vmt.t_free * (u_int64_t)page_size);
    printf("Available memory  : %ld\n", vmt.t_avm * (u_int64_t)page_size);

    return 0;
}

以下是该程序的输出,与我系统上的 vmstat(8) 输出进行了比较。
~/code/memstats % cc memstats.c 
~/code/memstats % ./a.out 
Free memory       : 5481914368
Available memory  : 8473378816
~/code/memstats % vmstat 
 procs      memory      page                    disks     faults         cpu
 r b w     avm    fre   flt  re  pi  po    fr  sr ad0 ad1   in   sy   cs us sy id
 0 0 0   8093M  5228M   287   0   1   0   304 133   0   0  112 9597 1652  2  1 97

sysctl: "在Linux 5.5和glibc 2.32中已被移除。建议使用syscall。" - undefined

3

Linux当前可用内存: sysconf(_SC_AVPHYS_PAGES)get_avphys_pages()

总RAM大小可以使用sysconf(_SC_PHYS_PAGES);https://dev59.com/HnE85IYBdhLWcg3w_I38#2513561上查询。

无论是sysconf(_SC_AVPHYS_PAGES)还是get_avphys_pages()都是POSIX的glibc扩展,它们提供了当前可用RAM页的总数。

然后,您只需将它们乘以sysconf(_SC_PAGE_SIZE)就可以获得当前可用的空闲RAM。

最小可运行示例请参见:C - 检查可用的免费RAM?


3

这个功能的“官方”函数是 std::get_temporary_buffer()。然而,你可能想测试一下你的平台是否有一个好的实现。我了解到并非所有平台都表现得如所需。


4
这个函数在C++17中已经被废弃,在C++20中已被移除。 - Elias

1

以下是在Linux平台上获取可用内存的提案:

/// Provides the available RAM memory in kibibytes (1 KiB = 1024 B) on Linux platform (Available memory in /proc/meminfo)
/// For more info about /proc/meminfo : https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-meminfo
long long getAvailableMemory()
{
  long long memAvailable = -1;
  std::ifstream meminfo("/proc/meminfo");
  std::string line;
  while (std::getline(meminfo, line))
  {
    if (line.find("MemAvailable:") != std::string::npos)
    {
      const std::size_t firstWhiteSpacePos = line.find_first_of(' ');
      const std::size_t firstNonWhiteSpaceChar = line.find_first_not_of(' ', firstWhiteSpacePos);
      const std::size_t nextWhiteSpace = line.find_first_of(' ', firstNonWhiteSpaceChar);
      const std::size_t numChars = nextWhiteSpace - firstNonWhiteSpaceChar;
      const std::string memAvailableStr = line.substr(firstNonWhiteSpaceChar, numChars);
      memAvailable = std::stoll(memAvailableStr);
      break;
    }
  }

  return memAvailable;
}

1

与其猜测,您是否考虑让用户配置缓冲区使用多少内存,以及假设相对保守的默认值?这样,即使没有覆盖,您仍然可以运行(可能略慢),但如果用户知道应用程序可用X内存,则可以通过配置该数量来提高性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接