Ich denke, Ihr ursprüngliches Problem war malloc
konnte den angeforderten Speicher auf Ihrem System nicht zuweisen.
Warum dies passiert ist, hängt von Ihrem System ab.
Wenn ein Prozess geladen wird, wird ihm Speicher bis zu einer bestimmten Adresse zugewiesen, die der Systemhaltepunkt für den Prozess ist. Jenseits dieser Adresse wird der Speicher für den Prozess nicht abgebildet. Wenn also der Prozess den "Break"-Punkt "trifft", fordert er mehr Speicher vom System an, und eine Möglichkeit, dies zu tun, ist über den Systemaufruf sbrk
malloc
würde das unter der Haube tun, aber in Ihrem System ist es aus irgendeinem Grund fehlgeschlagen.
Das kann viele Gründe haben, zum Beispiel:
1) Ich denke, in Linux gibt es eine Grenze für die maximale Speichergröße. Ich glaube, es ist ulimit
und vielleicht triffst du das. Überprüfen Sie, ob es auf ein Limit eingestellt ist
2) Vielleicht war Ihr System zu stark ausgelastet
3) Ihr Programm macht eine schlechte Speicherverwaltung und Sie erhalten fragmentierten Speicher, also malloc
kann die angeforderte Chunk-Größe nicht erhalten.
4) Ihr Programm beschädigt den malloc
interne Datenstrukturen, d. h. schlechte Pointer-Nutzung
usw.
Der Heap ist normalerweise so groß wie der adressierbare virtuelle Speicher Ihrer Architektur.
Sie sollten die aktuellen Limits Ihres Systems mit ulimit -a
überprüfen Befehl und suche diese Zeile max memory size (kbytes, -m) 3008828
, sagt diese Zeile auf meinem OpenSuse 11.4 x86_64 mit ~3,5 GiB RAM, dass ich ungefähr 3 GB RAM pro Prozess habe.
Dann können Sie Ihr System wirklich testen, indem Sie dieses einfache Programm verwenden, um den maximal nutzbaren Speicher pro Prozess zu überprüfen:
#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;
}
Dieses Programm ruft Speicher in 100-MiB-Schritten ab, präsentiert den aktuell zugewiesenen Speicher, weist ihm Nullen zu und gibt dann den Speicher frei. Wenn das System nicht mehr Speicher bereitstellen kann, gibt es NULL zurück und zeigt die endgültige maximal nutzbare Menge an RAM an.
Der Vorbehalt ist, dass Ihr System in der Endphase stark Speicher auslagern wird. Abhängig von Ihrer Systemkonfiguration entscheidet sich der Kernel möglicherweise, einige Prozesse zu beenden. Ich verwende 100-MiB-Schritte, damit einige Apps und das System etwas Luft zum Atmen haben. Sie sollten alles schließen, was nicht abstürzen soll.
Davon abgesehen. In meinem System, wo ich das schreibe, ist nichts abgestürzt. Und das obige Programm meldet kaum dasselbe wie ulimit -a
. Der Unterschied besteht darin, dass der Speicher tatsächlich getestet wurde und zwar mit memset()
bestätigt, dass der Speicher gegeben und verwendet wurde.
Zum Vergleich auf einer Ubuntu 10.04x86-VM mit 256 MiB RAM und 400 MiB Swap war der ulimit-Bericht memory size (kbytes, -m) unlimited
und mein kleines Programm hat 524.288.000 Bytes gemeldet, was ungefähr der Summe aus RAM und Swap entspricht, wobei der RAM, der von anderer Software und dem Kernel verwendet wird, abgezogen wird.
Bearbeiten:Wie Adam Zalcman schrieb, ulimit -m
wird auf neueren 2.6 und höheren Linux-Kerneln nicht mehr berücksichtigt, also stehe ich korrigiert. Aber ulimit -v
wird geehrt. Für praktische Ergebnisse sollten Sie -m durch -v ersetzen und nach virtual memory (kbytes, -v) 4515440
suchen . Es scheint reiner Zufall zu sein, dass meine Suse-Box den Wert -m hatte, der mit dem übereinstimmt, was mein kleines Dienstprogramm gemeldet hat. Sie sollten bedenken, dass dies virtueller Speicher ist, der vom Kernel zugewiesen wird. Wenn der physische Arbeitsspeicher nicht ausreicht, wird Auslagerungsspeicher benötigt, um dies auszugleichen.
Wenn Sie wissen möchten, wie viel physischer Arbeitsspeicher verfügbar ist, ohne einen Prozess oder das System zu stören, können Sie
verwenden
long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;
Dies schließt Cache- und Pufferspeicher aus, sodass diese Zahl viel kleiner sein kann als der tatsächlich verfügbare Speicher. OS-Caches können ziemlich groß sein und ihre Räumung kann den benötigten zusätzlichen Speicher liefern, aber das wird vom Kernel gehandhabt.
Die Heap- und Speicherverwaltung ist eine Einrichtung, die von Ihrer C-Bibliothek (wahrscheinlich glibc) bereitgestellt wird. Es verwaltet den Heap und gibt Ihnen jedes Mal, wenn Sie malloc()
ausführen, Speicherblöcke zurück . Es kennt keine Heap-Größenbeschränkung:Jedes Mal, wenn Sie mehr Speicher anfordern, als auf dem Heap verfügbar ist, fragt es einfach den Kernel nach mehr (entweder mit sbrk()
oder mmap()
).
Standardmäßig gibt Ihnen der Kernel fast immer mehr Speicher, wenn Sie dazu aufgefordert werden. Das bedeutet, dass malloc()
gibt immer eine gültige Adresse zurück. Nur wenn Sie zum ersten Mal auf eine zugewiesene Seite verweisen, macht sich der Kernel tatsächlich die Mühe, eine Seite für Sie zu finden. Wenn es feststellt, dass es Ihnen keine geben kann, führt es einen OOM-Killer aus, der nach bestimmten Maßstäben als Badness bezeichnet wird (was die Größe des virtuellen Speichers Ihres Prozesses und seiner Kinder, das Nice-Level, die Gesamtlaufzeit usw. umfasst) wählt ein Opfer aus und sendet ihm einen SIGTERM
. Diese Speicherverwaltungstechnik wird Overcommit genannt und wird vom Kernel verwendet, wenn /proc/sys/vm/overcommit_memory
ist 0 oder 1. Siehe overcommit-accounting in der Kernel-Dokumentation für Details.
Durch Schreiben von 2 in /proc/sys/vm/overcommit_memory
Sie können das Overcommit deaktivieren. Wenn Sie das tun, prüft der Kernel tatsächlich, ob er Speicher hat, bevor er es verspricht. Dies ergibt malloc()
Zurückgeben von NULL, wenn kein Speicher mehr verfügbar ist.
Mit setrlimit()
können Sie auch den virtuellen Speicher begrenzen, den ein Prozess zuweisen kann und RLIMIT_AS
oder mit dem ulimit -v
Befehl. Unabhängig von der oben beschriebenen Overcommit-Einstellung wird der Kernel dies ablehnen und malloc()
, wenn der Prozess versucht, mehr Speicher als das Limit zuzuweisen wird NULL zurückgeben. Beachten Sie, dass im modernen Linux-Kernel (einschließlich der gesamten 2.6.x-Serie) die Beschränkung der residenten Größe (setrlimit()
mit RLIMIT_RSS
oder ulimit -m
Befehl) ist unwirksam.
Die folgende Sitzung wurde auf Kernel 2.6.32 mit 4 GB RAM und 8 GB Swap ausgeführt.
$ 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
$
Im obigen Beispiel könnte es niemals zu Auslagerungen oder OOM-Kills kommen, aber dies würde sich erheblich ändern, wenn der Prozess tatsächlich versuchen würde, den gesamten zugewiesenen Speicher zu berühren.
Um Ihre Frage direkt zu beantworten:Es sei denn, Sie haben das virtuelle Speicherlimit explizit mit ulimit -v
festgelegt Befehl, gibt es keine andere Heap-Größenbeschränkung als die physischen Ressourcen des Computers oder die logische Grenze Ihres Adressraums (relevant in 32-Bit-Systemen). Ihre glibc wird weiterhin Speicher auf dem Heap zuweisen und mehr und mehr vom Kernel anfordern, wenn Ihr Heap wächst. Schließlich kann es passieren, dass Sie schlecht auslagern, wenn der gesamte physische Speicher erschöpft ist. Sobald der Auslagerungsbereich erschöpft ist, wird ein zufälliger Prozess durch den OOM-Killer des Kernels beendet.
Beachten Sie jedoch, dass die Speicherzuweisung aus vielen weiteren Gründen fehlschlagen kann als Mangel an freiem Speicher, Fragmentierung oder Erreichen eines konfigurierten Limits. Der sbrk()
und mmap()
Aufrufe, die vom Zuordner von glib verwendet werden, haben ihre eigenen Fehler, z. der Programmabbruch erreichte eine andere, bereits belegte Adresse (z.B. Shared Memory oder eine zuvor mit mmap()
gemappte Seite ) oder die maximale Anzahl von Speicherzuordnungen des Prozesses wurde überschritten.