如何检查 Linux 上进程的堆大小

发布于 2024-12-19 03:25:15 字数 236 浏览 1 评论 0 原文

我正在编写一些代码,但它一直崩溃。后来,在挖掘转储后,我意识到我超出了最大堆限制(如果我添加了对 malloc 的检查,生活会更容易)。虽然我修复了这个问题,有什么方法可以增加我的堆大小吗?

PS:这里有一个非常类似的问题,但回复我不清楚。

I was writing some code and it kept crashing. Later after digging the dumps I realized I was overshooting the maximum heap limit (life would have been easier if I had added a check on malloc). Although I fixed that, is there any way to increase my heap size?

PS: A quite similar question here but the reply is unclear to me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

猫卆 2024-12-26 03:25:15

堆和内存管理是 C 库(可能是 glibc)提供的功能。每次执行 malloc() 时,它都会维护堆并返回内存块。它不知道堆大小限制:每次您请求的内存超过堆上可用的内存时,它都会向内核请求更多内存(使用 sbrk()mmap ())。

默认情况下,当需要时,内核几乎总是会为您提供更多内存。这意味着 malloc() 将始终返回有效地址。只有当您第一次引用分配的页面时,内核才会真正为您查找页面。如果它发现它不能给你一个,它就会运行一个 OOM 杀手,它根据某种称为“坏度”的衡量标准(包括你的进程及其子进程的虚拟内存大小、良好级别、总体运行时间等)选择一个受害者并向其发送一个SIGTERM。这种内存管理技术称为过量使用,当 /proc/sys/vm/overcommit_memory 为 0 或 1 时,内核将使用该技术。请参阅 overcommit-accounting 位于内核文档中以获取详细信息。

通过将 2 写入 /proc/sys/vm/overcommit_memory 可以禁用过度使用。如果你这样做,内核实际上会在承诺之前检查它是否有内存。如果没有更多可用内存,这将导致 malloc() 返回 NULL。

您还可以使用 setrlimit()RLIMIT_AS 或使用 ulimit -v 命令设置进程可以分配的虚拟内存限制。无论上述过度使用设置如何,如果进程尝试分配超过限制的内存,内核将拒绝它,并且 malloc() 将返回 NULL。请注意,在现代 Linux 内核(包括整个 2.6.x 系列)中,驻留大小的限制(setrlimit()RLIMIT_RSSulimit -m > 命令)无效。

下面的会话在内核 2.6.32 上运行,具有 4GB RAM 和 8GB 交换空间。

$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>

int main() {
  int i = 0;
  for (; i < 13*1024; i++) {
    void* p = malloc(1024*1024);
    if (p == NULL) {
      fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
      return 1;
    }
  }
  printf("Allocated it all\n");
  return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$

在上面的示例中,交换或 OOM Kill 永远不会发生,但如果进程实际上尝试接触所有分配的内存,情况就会发生重大变化。

直接回答您的问题:除非您使用 ulimit -v 命令明确设置了虚拟内存限制,否则除了计算机的物理资源或地址空间的逻辑限制(与 32-32 相关)之外,没有堆大小限制位系统)。你的 glibc 将继续在堆上分配内存,并随着堆的增长向内核请求越来越多的内存。如果所有物理内存都耗尽,最终可能会导致交换效果不佳。一旦交换空间耗尽,内核的 OOM 杀手就会杀死一个随机进程。

但请注意,除了缺少可用内存、碎片或达到配置的限制之外,内存分配失败的原因可能还有很多。 glib 分配器使用的 sbrk()mmap() 调用都有自己的失败,例如程序中断到达另一个已经分配的地址(例如共享内存或之前的页面)使用 mmap() 映射)或已超出进程的最大内存映射数。

Heap and memory management is a facility provided by your C library (likely glibc). It maintains the heap and returns chunks of memory to you every time you do a malloc(). It doesn't know heap size limit: every time you request more memory than what is available on the heap, it just goes and asks the kernel for more (either using sbrk() or mmap()).

By default, kernel will almost always give you more memory when asked. This means that malloc() will always return a valid address. It's only when you refer to an allocated page for the first time that the kernel will actually bother to find a page for you. If it finds that it cannot hand you one it runs an OOM killer which according to certain measure called badness (which includes your process's and its children's virtual memory sizes, nice level, overall running time etc) selects a victim and sends it a SIGTERM. This memory management technique is called overcommit and is used by the kernel when /proc/sys/vm/overcommit_memory is 0 or 1. See overcommit-accounting in kernel documentation for details.

By writing 2 into /proc/sys/vm/overcommit_memory you can disable the overcommit. If you do that the kernel will actually check whether it has memory before promising it. This will result in malloc() returning NULL if no more memory is available.

You can also set a limit on the virtual memory a process can allocate with setrlimit() and RLIMIT_AS or with the ulimit -v command. Regardless of the overcommit setting described above, if the process tries to allocate more memory than the limit, kernel will refuse it and malloc() will return NULL. Note than in modern Linux kernel (including entire 2.6.x series) the limit on the resident size (setrlimit() with RLIMIT_RSS or ulimit -m command) is ineffective.

The session below was run on kernel 2.6.32 with 4GB RAM and 8GB swap.

$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>

int main() {
  int i = 0;
  for (; i < 13*1024; i++) {
    void* p = malloc(1024*1024);
    if (p == NULL) {
      fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
      return 1;
    }
  }
  printf("Allocated it all\n");
  return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$

In the example above swapping or OOM kill could never occur, but this would change significantly if the process actually tried to touch all the memory allocated.

To answer your question directly: unless you have virtual memory limit explicitly set with ulimit -v command, there is no heap size limit other than machine's physical resources or logical limit of your address space (relevant in 32-bit systems). Your glibc will keep allocating memory on the heap and will request more and more from the kernel as your heap grows. Eventually you may end up swapping badly if all physical memory is exhausted. Once the swap space is exhausted a random process will be killed by kernel's OOM killer.

Note however, that memory allocation may fail for many more reasons than lack of free memory, fragmentation or reaching a configured limit. The sbrk() and mmap() calls used by glib's allocator have their own failures, e.g. the program break reached another, already allocated address (e.g. shared memory or a page previously mapped with mmap()) or process's maximum number of memory mappings has been exceeded.

清欢 2024-12-26 03:25:15

堆通常与体系结构上的可寻址虚拟内存一样大。

您应该使用 ulimit -a 命令检查系统当前限制,并查找此行 max memory size (kbytes, -m) 3008828,此行在我的 OpenSuse 11.4 x86_64 上~3.5 GiB 的 ram 表示每个进程大约有 3GB 的 ram。

然后,您可以使用这个简单的程序来检查每个进程的最大可用内存来真正测试您的系统:

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

int main(int argc,char* argv[]){
        size_t oneHundredMiB=100*1048576;
        size_t maxMemMiB=0;
        void *memPointer = NULL;
        do{
                if(memPointer != NULL){
                        printf("Max Tested Memory = %zi\n",maxMemMiB);
                        memset(memPointer,0,maxMemMiB);
                        free(memPointer);
                }
                maxMemMiB+=oneHundredMiB;
                memPointer=malloc(maxMemMiB);
        }while(memPointer != NULL);
        printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB);
        return 0;
}

该程序以 100MiB 增量获取内存,显示当前分配的内存,在其上分配 0,然后释放内存。当系统无法提供更多内存时,返回 NULL 并显示最终最大可用内存量。

需要注意的是,您的系统将在最后阶段开始大量交换内存。根据您的系统配置,内核可能会决定终止某些进程。我使用 100 MiB 增量,因此某些应用程序和系统有一些喘息空间。你应该关闭任何你不想崩溃的东西。

话虽这么说。在我写这篇文章的系统中没有任何崩溃。上面的程序报告与 ulimit -a 几乎相同。不同之处在于,它实际上测试了内存,并通过 memset() 确认内存已给出并使用。

为了与具有 256 MiB 内存和 400MiB 交换空间的 Ubuntu 10.04x86 VM 进行比较,ulimit 报告为内存大小(kbytes,-m)无限制,而我的小程序报告了 524.288.000 字节,这大约是组合内存和交换,折扣其他软件和内核使用的内存。

编辑:正如 Adam Zalcman 所写,ulimit -m 在较新的 2.6 及更高版本的 Linux 内核上不再受尊重,所以我纠正了。但 ulimit -v 受到尊重。为了获得实际结果,您应该将 -m 替换为 -v,并查找虚拟内存(千字节,-v)4515440。我的 suse 盒子的 -m 值与我的小实用程序报告的值一致,这似乎只是偶然。你应该记住,这是内核分配的虚拟内存,如果物理内存不足,则需要交换空间来弥补。

如果您想在不干扰任何进程或系统的情况下知道有多少物理内存可用,您可以使用

long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;

这将排除缓存和缓冲内存,所以这个数字可能远小于实际可用内存。操作系统缓存可能非常大,它们的驱逐可以提供所需的额外内存,但这是由内核处理的。

The heap usually is as large as the addressable virtual memory on your architecture.

You should check your systems current limits with the ulimit -a command and seek this line max memory size (kbytes, -m) 3008828, this line on my OpenSuse 11.4 x86_64 with ~3.5 GiB of ram says I have roughly 3GB of ram per process.

Then you can truly test your system using this simple program to check max usable memory per process:

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

int main(int argc,char* argv[]){
        size_t oneHundredMiB=100*1048576;
        size_t maxMemMiB=0;
        void *memPointer = NULL;
        do{
                if(memPointer != NULL){
                        printf("Max Tested Memory = %zi\n",maxMemMiB);
                        memset(memPointer,0,maxMemMiB);
                        free(memPointer);
                }
                maxMemMiB+=oneHundredMiB;
                memPointer=malloc(maxMemMiB);
        }while(memPointer != NULL);
        printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB);
        return 0;
}

This programs gets memory on 100MiB increments, presents the currently allocated memory, allocates 0's on it,then frees the memory. When the system can't give more memory, returns NULL and it displays the final max usable amount of ram.

The Caveat is that your system will start to heavily swap memory in the final stages. Depending on your system configuration, the kernel might decide to kill some processes. I use a 100 MiB increments so there is some breathing space for some apps and the system. You should close anything that you don't want crashing.

That being said. In my system where I'm writing this nothing crashed. And the program above reports barely the same as ulimit -a. The difference is that it actually tested the memory and by means of memset() confirmed the memory was given and used.

For comparison on a Ubuntu 10.04x86 VM with 256 MiB of ram and 400MiB of swap the ulimit report was memory size (kbytes, -m) unlimited and my little program reported 524.288.000 bytes, which is roughly the combined ram and swap, discounting ram used by others software and the kernel.

Edit: As Adam Zalcman wrote, ulimit -m is no longer honored on newer 2.6 and up linux kernels, so i stand corrected. But ulimit -v is honored. For practical results you should replace -m with -v, and look for virtual memory (kbytes, -v) 4515440. It seems mere chance that my suse box had the -m value coinciding with what my little utility reported. You should remember that this is virtual memory assigned by the kernel, if physical ram is insufficient it will take swap space to make up for it.

If you want to know how much physical ram is available without disturbing any process or the system, you can use

long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;

this will exclude cache and buffer memory, so this number can be far smaller than the actual available memory. OS caches can be quiet large and their eviction can give the needed extra memory, but that is handled by the kernel.

凹づ凸ル 2024-12-26 03:25:15

我认为您最初的问题是 malloc 未能在您的系统上分配请求的内存。

发生这种情况的原因取决于您的系统。

当进程被加载时,它会被分配内存到某个地址,该地址是进程的系统断点。超出该地址的内存对于进程来说是未映射的。因此,当进程“到达”“中断”点时,它会向系统请求更多内存,实现此目的的一种方法是通过系统调用 sbrk
malloc 会在后台执行此操作,但在您的系统中由于某种原因它失败了。

造成这种情况的原因可能有很多,例如:
1)我认为在Linux中最大内存大小是有限制的。我认为它是ulimit,也许你击中了它。检查是否设置了限制
2) 也许您的系统负载过高
3) 您的程序的内存管理不善,最终导致内存碎片,因此 malloc 无法获取您请求的块大小。
4) 你的程序破坏了 malloc 内部数据结构,即错误的指针使用
ETC

I think your original problem was that malloc failed to allocate the requested memory on your system.

Why this happened is specific to your system.

When a process is loaded, it is allocated memory up to a certain address which is the system break point for the process. Beyond that address the memory is unmapped for the process. So when the process "hits" the "break" point it requests more memory from the system and one way to do this is via the system call sbrk
malloc would do that under the hood but in your system for some reason it failed.

There could be many reasons for this for example:
1) I think in Linux there is a limit for max memory size. I think it is ulimit and perhaps you hit that. Check if it is set to a limit
2) Perhaps your system was too loaded
3) Your program does bad memory management and you end up with fragemented memory so malloc can not get the chunk size you requested.
4) Your program corrupts the malloc internal data structures i.e. bad pointer usage
etc

无所的.畏惧 2024-12-26 03:25:15

我想对之前的答案补充一点。

应用程序有这样的错觉:malloc() 返回“实心”块;实际上,缓冲区可能分散、分散地存在于 RAM 的许多页上。这里的关键事实是:进程的虚拟内存(包含其代码或包含大型数组的内容)必须是连续的。我们甚至承认代码和数据是分开的;大数组 char str[universe_size] 必须是连续的。

现在:单个应用程序可以任意扩大堆以分配这样的数组吗?

如果机器中没有运行其他任何东西,答案可能是“是”。堆可以大得离谱,但它必须有边界。在某些时候,对 sbrk()(在 Linux 中,简而言之,“扩大”堆的函数)的调用可能会偶然发现为另一个应用程序保留的区域。

链接提供了一些有趣且清晰的示例,请查看。我没有找到有关Linux的信息。

I'd like to add one point to the previous answers.

Apps have the illusion that malloc() returns 'solid' blocks; in reality, a buffer may exist scattered, pulverized, on many pages of RAM. The crucial fact here is this: the Virtual Memory of a process, containing its code or containing something as a large array, must be contiguous. Let's even admit that code and data be separated; a large array, char str[universe_size], must be contiguous.

Now: can a single app enlarge the heap arbitrarily, to alloc such an array?

The answer could be 'yes' if there were nothing else running in the machine. The heap can be ridiculously huge, but it must have boundaries. At some point, calls to sbrk() (in Linux, the function that, in short, 'enlarges' the heap) should stumble on the area reserved for another application.

This link provides some interesting and clarifying examples, check it out. I did not find the info on Linux.

-小熊_ 2024-12-26 03:25:15

您可以从顶部找到您的 webapp/java 进程的进程 ID。
使用 jmap heap - 获取堆分配。我在 AWS-Ec2 上对 Elastic beanstalk 进行了测试,它给出了分配的堆。这是详细的答案
通过环境属性在elasticbean stalk中进行Xmx设置

You can find the process id of your webapp/java process from top.
Use jmap heap - to get the heap allocation. I tested this on AWS-Ec2 for elastic beanstalk and it gives the heap allocated. Here is the detailed answer
Xmx settings in elasticbean stalk through environment properties

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文