So überprüfen Sie die Heap-Größe für einen Prozess unter Linux

Lesezeit: 9 Minuten

Benutzeravatar von bashrc
bashrc

Ich schrieb einen Code und es stürzte immer wieder ab. Später, nachdem ich die Dumps ausgegraben hatte, stellte ich fest, dass ich die maximale Heap-Grenze überschritten hatte (das Leben wäre einfacher gewesen, wenn ich malloc überprüft hätte). Obwohl ich das behoben habe, gibt es eine Möglichkeit, meine Heap-Größe zu erhöhen?

PS: Eine ganz ähnliche Frage hier, aber die Antwort ist mir unklar.

  • Ist dem Heap nicht fast der gesamte virtuelle Speicher für Ihr Programm zugewiesen? Handelt es sich um eine 32-Bit-Plattform (oder kleiner) und wie viel Speicher möchten Sie zuweisen?

    – Emil Vikström

    3. Dezember 2011 um 10:25 Uhr


Benutzeravatar von Adam Zalcman
Adam Zalcman

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 a tun, Teile des Speichers zurück malloc(). Es kennt keine Heap-Größenbeschränkung: Jedes Mal, wenn Sie mehr Speicher anfordern, als auf dem Heap verfügbar ist, geht es einfach und fragt 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 er feststellt, dass er dir keinen aushändigen kann, läuft er einen OOM-Killer, der nach bestimmten Maßen aufgerufen hat Schlechtigkeit (was die virtuellen Speichergrößen Ihres Prozesses und seiner Kinder, das nette Level, die Gesamtlaufzeit usw. enthält) wählt ein Opfer aus und sendet ihm a SIGTERM. Diese Speicherverwaltungstechnik wird Overcommit genannt und wird vom Kernel verwendet, wenn /proc/sys/vm/overcommit_memory 0 oder 1 ist. Siehe Overcommit-Buchhaltung in der Kernel-Dokumentation für Details.

Durch das 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 wird dazu führen malloc() Zurückgeben von NULL, wenn kein Speicher mehr verfügbar ist.

Sie können auch den virtuellen Speicher begrenzen, den ein Prozess zuweisen kann setrlimit() und RLIMIT_AS oder mit dem ulimit -v Befehl. Unabhängig von der oben beschriebenen Overcommit-Einstellung wird der Kernel dies ablehnen, wenn der Prozess versucht, mehr Speicher als das Limit zuzuweisen malloc() wird NULL zurückgeben. Beachten Sie als im modernen Linux-Kernel (einschließlich der gesamten 2.6.x-Serie) die Begrenzung 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 niemals ein Swapping oder OOM-Kill auftreten, 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 festgelegt ulimit -v 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 vom 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. Das sbrk() und mmap() Aufrufe, die von glibs Allokator verwendet werden, haben ihre eigenen Fehler, z mmap()) oder die maximale Anzahl von Speicherzuordnungen des Prozesses wurde überschritten.

  • Ist es möglich zu bekommen start_brk direkt aus dem Userland, wenn Sie den Rückgabewert von nicht gespeichert haben sbrk Anrufe?

    – Ciro Santilli OurBigBook.com

    29. April 2015 um 21:56 Uhr

Benutzeravatar von RedComet
Roter Komet

Der Heap ist normalerweise so groß wie der adressierbare virtuelle Speicher Ihrer Architektur.

Sie sollten die Stromgrenzen Ihres Systems mit dem überprüfen ulimit -a Befehl und suche diese Zeile max memory size (kbytes, -m) 3008828sagt 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 mit diesem einfachen Programm wirklich testen, 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 geben 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 und durch memset() bestätigt, dass der Speicher gegeben und verwendet wurde.

Zum Vergleich auf einer Ubuntu 10.04×86-VM mit 256 MiB RAM und 400 MiB Swap diente 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 von anderer Software und dem Kernel verwendete RAM 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 suchen virtual memory (kbytes, -v) 4515440. 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.

  • ulimit -m hat keine Auswirkungen auf Linux-Kernel neuer als 2.4.29.

    – Adam Zalcman

    3. Dezember 2011 um 11:39 Uhr

  • “unbegrenzt” macht keinen Sinn, oder? Es muss eine Grenze geben. Das ulimit -m und das ulimit -v geben beide auf meinem Ubuntu unbegrenzt zurück. Ich denke, der perfekte Weg, dies herauszufinden, besteht darin, Ihr Dienstprogramm auszuführen.

    – Bikash Gyawali

    11. Dezember 2013 um 16:16 Uhr

Ich denke, dein ursprüngliches Problem war das 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 denke, 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 hat eine schlechte Speicherverwaltung und Sie erhalten so einen fragmentierten Speicher malloc kann die angeforderte Chunk-Größe nicht erhalten.
4) Ihr Programm beschädigt die malloc interne Datenstrukturen, dh schlechte Pointer-Nutzung
etc

  • Antwort 3 war der Fall. Ich habe versucht, ulimits zu überprüfen, aber nichts für die Heap-Größe gefunden. Ja, ich kann die Stack-Größe mit ulimit erhöhen )

    – bashrc

    3. Dezember 2011 um 11:13 Uhr

Ich möchte einen Punkt zu den vorherigen Antworten hinzufügen.

Apps haben die Illusion, dass malloc() „solide“ Blöcke zurückgibt; in Wirklichkeit kann ein Puffer verstreut, pulverisiert, auf vielen Seiten des RAM existieren. Die entscheidende Tatsache hier ist: Der virtuelle Speicher eines Prozesses, der seinen Code enthält oder etwas als großes Array enthält, muss zusammenhängend sein. Lassen Sie uns sogar zugeben, dass Code und Daten getrennt werden; ein großes Array, char str[universe_size]müssen zusammenhängend sein.

Nun: Kann eine einzelne App den Heap beliebig vergrößern, um ein solches Array zuzuweisen?

Die Antwort könnte “ja” lauten, wenn nichts anderes in der Maschine läuft. Der Haufen kann lächerlich groß sein, aber er muss Grenzen haben. Irgendwann sollten Aufrufe von sbrk() (in Linux die Funktion, die kurz gesagt den Heap ‘vergrößert’) auf den Bereich stoßen, der für eine andere Anwendung reserviert ist.

Dies Verknüpfung bietet einige interessante und verdeutlichende Beispiele, schau es dir an. Ich habe die Informationen zu Linux nicht gefunden.

Die Prozess-ID Ihres Webapp-/Java-Prozesses finden Sie oben. Verwenden Sie jmap heap – um die Heap-Zuweisung zu erhalten. Ich habe dies auf AWS-Ec2 für Elastic Beanstalk getestet und es gibt den zugewiesenen Heap an. Hier ist die detaillierte Antwort Xmx-Einstellungen in Elasticbean Stalk durch Umgebungseigenschaften

1432740cookie-checkSo überprüfen Sie die Heap-Größe für einen Prozess unter Linux

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy