Dieser Beitrag ist aus der Kategorie:
„Was kann da schon schief gehen“
Speicher ist billig, aber doch endlich und nicht beliebig nachrüstbar. Es kann also Situationen geben, wo man Speicher einsparen muß, weil andere der irrigen Meinung, waren, das mit dem Speicherverbrauch wäre kein Problem.
Aber der Reihe nach: Was ist überhaupt das Problem ?
PHP als CGI ausgeführt, verbraucht pro Start > 400 MB Speicher fürs Nichtstun.
Beispiel: „php-cgi -a“ startet PHP ohne irgend was zu machen.
In einer zweiten Konsole läßt man sich mit pmap anzeigen, wer in dem Prozess was an Speicher belegt:
pmap {pid of process}
Beispielausgabe ( gekürzt : Warum kommt später )
[root@xxx]# pmap 5408
5408: php-cgi -a
0000560d00b44000 3724K r-x– php-cgi
0000560d010e6000 536K r—- php-cgi
0000560d0116c000 16K rw— php-cgi
…
00007fce773f8000 28K r-x– libcrypt-2.23.so
00007fce773ff000 2044K —– libcrypt-2.23.so
00007fce775fe000 4K r—- libcrypt-2.23.so
00007fce775ff000 4K rw— libcrypt-2.23.so
…
total 420372K
Genau, 410 MB und nicht eine Anweisung ausgeführt. 3.7 MB gehen für den PHP-Interpretercode selbst drauf, ok. 2 MB gehen alleine für die libcrypt drauf.
Und jetzt der Grund wieso die Ausgabe gekürzt ist: PHP benutzt 75 Libs => ~ 160 MB Speicherverbrauch … bei jedem Script!
Bei der Analyse ist aber aufgefallen, daß auch eine Locale eingebunden wird:
-rw-r–r– 1 root root 110562112 22. Dez 12:01 /usr/lib/locale/locale-archive
-rw-r–r– 1 root root 0 22. Dez 12:01 /usr/lib/locale/locale-archive.tmpl
Auch bekannt als 107 MB und diese Datei wird in jeden PHP Prozess geladen, der läuft. Keine Gnade.
Locale – was sind das ?
„Locals“ sind keine Eingeborenen, sondern nur die Informationen über jene, also Zahlensysteme, Schreibrichtung, Datumsformat, Gewichte, Längen usw. . Wer mal sehen will, welche auf seinem Linux installiert sind, gibt das ein : locale -a
Da kommen Sachen von Sprachen, wo man nicht mal sagen kann, wo die passenden Länder sind 😉
Irgendwann hat Red Hat mal entschieden, daß die einfach alle Sprachen des Planeten ausliefern, weil dann der User nicht mehr machen muß. Stimmt.
Auf einem Desktopsystem ok. Aber auf einem Server ist das nicht ok und deswegen muß das auf ein gesundes Maß gedrückt werden. Das macht man mit : /usr/sbin/build-locale-archive -v -l „de:en:ru:jp“
„Oh… geht ja gar nicht“ ????
Doch geht, aber nur mit Trick Siebzehn aus der „Erschiesst den Trottel Ecke“.
Die obige Locale-Archiv-Datei kommt mit dem Paket „glibc-all-langpacks“ auf den Server. Damit kommt aber auch ein TEMPLATE File mit, aus dem man sich genau das Archiv selbst bauen kann, wenn man eben nicht alles haben will.
…
Ihr habt’s erfaßt, das wäre zu einfach gewesen 🙁
Die Templatedatei ist nämlich LEER und damit nicht zu gebrauchen. Ihr Fragt Euch jetzt : „Wie bekommt man so ein Template?“ hmm.. Tja.. aus dem Repo jedenfalls nicht und doch, bekommt man. Jetzt wird es lustig, wir kopieren das vorhandene Archiv als Template für sich selbst ! Ja, richtig, mit der Faust durchs Auge: In your Face Red Hat!
Also eingegeben:
cp locale-archive locale-archive.tmpl
/usr/sbin/build-locale-archive -v -l „de:en:ru:jp“
locale -a
und schon ist das File keine 107 MB mehr sondern nur noch 7 MB und auch das halte ich für extrem viel Schrott für 4 Sprachen. Aber jetzt lädt das php nur noch 7 MB rein. Das macht in der Endabrechnung dann 25% weniger RAM Verbrauch.
Als nächstes muß man rausfinden, wieso die Libs jeweils 2 MB verbrennen, wenn die nur 70k groß sind. Aber das ist eine andere Geschichte aus dem |-: La-La-La-Land 😐 .
„160 MB Speicherverbrauch … bei jedem Script!“
Du weißt was der Vorteil von shared libraries ist? Weißt du was mmap macht? Man verbraucht den Speicher eben nicht pro Prozess sondern nur einmal.
Schöner Beitrag.
Ich finde solche Analysen immer wieder interessant.
Der Artikel unterschlägt, dass es sich bei den von pmap angezeigten *.so um shared objects handelt. Alle Prozesse benutzen die gleiche Instanz einer einmal geladenen Lib. Das sieht man gut, wenn man sich die Adressen anschaut, auf denen die jeweilige Lib in den Prozessspeicher gemappt wird:
# pmap $(pgrep -f php-fpm) | grep libcrypto
00007fd86064b000 1844K r-x– libcrypto.so.1.0.0
00007fd860818000 2044K —– libcrypto.so.1.0.0
00007fd860a17000 116K r—- libcrypto.so.1.0.0
00007fd860a34000 64K rw— libcrypto.so.1.0.0
00007fd86064b000 1844K r-x– libcrypto.so.1.0.0
00007fd860818000 2044K —– libcrypto.so.1.0.0
00007fd860a17000 116K r—- libcrypto.so.1.0.0
00007fd860a34000 64K rw— libcrypto.so.1.0.0
00007fd86064b000 1844K r-x– libcrypto.so.1.0.0
00007fd860818000 2044K —– libcrypto.so.1.0.0
00007fd860a17000 116K r—- libcrypto.so.1.0.0
00007fd860a34000 64K rw— libcrypto.so.1.0.0
00007fd86064b000 1844K r-x– libcrypto.so.1.0.0
00007fd860818000 2044K —– libcrypto.so.1.0.0
00007fd860a17000 116K r—- libcrypto.so.1.0.0
00007fd860a34000 64K rw— libcrypto.so.1.0.0
Die von pmap angezeigten rund 400 MB sind also mit Vorsicht zu genießen!
Der Tipp zur Reduzierung der Locales ist Klasse!
Das sollte man meinen, aber 420MB – 160 MB SO, sind immernoch 260 MB, wo man sich fragt: Wofür eigentlich ?
Du redest über Speicherverbrauch und guckst dir dann mit pmap eine Zahl an, die du für den Speicherverbrauch hältst.
Blöd daran ist nur, dass dir pmap den virtual adress space zeigt und eben nicht den Speicherverbrauch.
Mach mal ein top auf. Da findest du zwei Spalten. Die Spalte, über die du reden willst, ist RES. Und die Spalte, über die du jetzt aus versehen geredet hast, ist VIRT. Das kann sich schonmal um nen Gigabyte voneinander unterscheiden.
Beispiel: pidgin. Verbraucht 40 MB Speicher bei einem virtual adress space von bis zu einem Gigabyte.
Ich habe mal dein php-cgi -a in einer CentOS 7 VM ausgeführt.
Ergebnis:
228 MB virtual adress space und 8 Kilobyte (!) tatsächlicher Speicherverbrauch.
Die Zahlen kann ich auf F24 nicht reproduzieren, selbst die kleinsten RES+SHR Werte liegen bei ~50 MB pro Instanz. Es kann natürlich sein, daß andere Libs/PHP Module geladen werden, als auf dem CentOS.