[Megoldva] Firefox memóriahasználat - cgroups

Fórumok

Egy általam karbantartott Fedora 21-en azt vettem észre, lassan halad a frissítés, néztem egy top-ot, s azt tapasztaltam, hogy a firefox process virtuális memóriaként röpke 14.145 GB-ot foglalt, ténylegesen ki is használt, azaz a kernel alálapozott 1.009 GB-ot, miközben a gép fizikai memóriája 2 GB, meg majdnem 4 GB swap. Tudom, a kernel optimista, ad memóriát, ha kérnek tőle, aztán reménykedik, hogy ténylegesen nem használja az alkalmazás, ha meg igen, gyorsan alálapoz fizikai RAM-ot.

Ugyanakkor erős túlzásnak érzem egy böngészőtől, hogy legfeljebb 10 nyitott fül mellett elkérjen több, mint 14 GB memóriát. Azt nem tudom, hogy a böngészőben van memory-leak, valamelyik kiterjesztés vacak, vagy valamelyik weblap java-scriptje ámokfut, de nem is érdekel. Sohasem használtam a cgroups lehetőségeit, és érzem, hogy szeretném. Még nem olvastam el a dokumentációt, de aki csinált már ilyet, az írhatna hasznos gyakorlati tanácsokat, mit is érdemes csinálni, s hogyan, hogy korlátok közé szorítsam a Firefox memóriahasználatát.

Hozzászólások

Itt kezdheted a vizsgálódást: about:memory

Ugye a 2+4 < 14 ? Tehát az igényelt memória 14GB, a felhasznált 1GB. Ez nem kevés, de 10 fülre könnyen összejöhet.
Ugyanez windózon 4-10 fülre 0.03/0.5 (peak). Amire nincs lehetőséged: memoryfox.

A gyorsítótár méretét érdemes korlátozni - nálam 40MB.
A memória felhasználását csökkenti az adblock* is.

Azért tudok olyan elmebeteg oldalt, amelyhez 2GB fizika memória már elég lehet. :)

Első körben a firefox beállításait nézd át. Nagyon memóriahasználattal, előtöltéssel és hasonlókkal kapcsolatos cucc van, amiket kikapcsolva lehet, hogy lassabb lesz (nem minden esetben, haverom csinálta meg egyszer, mert régi gépe volt kevés erőforrással, nála sokkal használhatóbb lett a végeredmény), de nem fog ennyi memóriát enni.
Ha használsz adblock-ot az 40 mega/fül, plusz 4-5/iframe...
Persze ezen felül is lehet próbálkozni korlátozni, de félek tőle egy not enough memory hibával és egy működésképtelen firefoxxal végzed majd.

FathoM

Ha 64 bites firefoxod van, akkor nem kell meglepődni, a 64 bites alkalmazások kapásból befoglalnak maguknak egy jó nagy címtartományt (ilyen 1x giga körül) de ettől még nem fognak több memóriát használni. Épp ezért a top átverős ilyen szempontból.

Igen, 64 bites a Firefox. Tényleg, most megnéztem két gépen, az egyiken 1.2 GB-ot, a másikon 1.3 GB-ot foglalt, de ténylegesen 220 MB-ot illetve 328 MB-ot használnak jelenleg. Ugyanakkor abból indultam ki, hogy cgroups-szal lehetne korlátot szabni neki, amit úgy élne meg, mintha kevesebb fizikai RAM-hoz kellene alkalmazkodnia. Azért a 14 GB foglalását túlzónak érzem, mégpedig azért, mert ha kitalálja, hogy elkezdi felhasználni, akkor a kernel nyilván intenzív swap-elésre kényszerül, amellyel lassúvá válik a gép. Aztán, ha az sem elég, jön az OOM killer, és reménykedhetek, hogy jól becsül, és a Firefoxot vágja ki, nem pedig valami fontos rendszer komponenst. Vagy teszem azt, épp a yum-ot frissítés közben, mert a függőségek feloldásához, rpm adatbázisban túrkáláshoz viszonylag sok memóriát használ, az meg igen kellemetlen, ha félig frissített csomagok közben lelőné a kernel a process-t.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

na az nyaloka. De aaaaa nem akar a systemd egyeduralomra torni....
amint lesz szabadidom maris atterek Gentoo-ra. Ott legalabb az van amit en csinalok es nem fal fel semmi semmit csak ha en engedem.

azert kar hogy a piros es kek kalaposok sem tesznek semmit ennek a folyamatnak a megallitasa erdekeben.

legyen systemd-os es kesz a tobbieket meg hagyjak beken.
--
A legértékesebb idő a pillanat amelyben élsz.
https://sites.google.com/site/jupiter2005ster/

Nekem semmi bajom nem volt a systemd-vel úgy, mint egy új init rendszer. De ez már kezd ijesztő lenni. Lassan tényleg a kernel is egy systemd modul lesz, de ha így megy tovább, az alkalmazások is. Gondolom, nézted, amit linkeltem. Valaki panaszolta, hogy nincs cgred daemon, mire megkapta, hogy ez nem bug:

Control Group Interface in Systemd is replacement.

Remek.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Dehogy falta fel, ez megint a FUD. Annyi, hogy ő maga megfelelő cgroup-ban tudja indítani a daemont, nincs szükség arra, hogy a cgred folyamatos szkenneléssel osztályozza a futó daemonokat, mert az sysvinit scriptje képtelen cgroup-ban indításra. A cgred-et váltotta le, ami meg is érett a pusztulásra, egy átmeneti megoldás volt.


# yum install libcgroup-tools
# man 1 cgexec

Aztán a /sys/fs/cgroup -ba szépen beállíthatod a limiteket.

Mi az hogy "mit kell nézni?" :) Semmit nem kell nézni, a cgexec egy adott cgroup-ban indítja a firefox-ot, és az összes thread-je, fork-ja ott is marad, nem tud kiszabadulni.


$ sudo cgcreate -t dap -a dap -g memory:firefox
$ cgexec -g memory:firefox firefox
$ awk '/^total_rss /{ print $2/1024**2"MB" }' /sys/fs/cgroup/memory/firefox/memory.stat 
170.066MB
$ echo 2G >/sys/fs/cgroup/memory/firefox/memory.limit_in_bytes

A cgcreate/echo persze kiváltható azzal ha a cgconfig.conf -ban adod meg a group-okat statikusan.

„Kedves” ember ez a Linus. Ha jól értettem - javíts ki, ha mégsem -, a userspace felé mindenképp megtartják a kompatibilitást. Ebből viszont arra következtetek, hogy lehet, kellene írnom egy egyszerű wrapper scriptet, amely a megfelelő ulimit-et követően indítja a Firefoxot. Erről jut eszembe, ha ulimit után exec-cel forkolok firefox-ra, megtartva a shell pid-jét a firefox számára, az ulimit már érvényes lesz, vagy önálló gyermekfolyamatot kell mindenképp indítani, s a szülő shellnek a memóriában kell maradnia? Nyilván az első jobban tetszene.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Jó.

1.

ulimit valami
exec firefox
#------------------------------

2.

ulimit valami
firefox
exit
#------------------------------

1 vagy 2? Én 1-re gondolok, de ilyenkor a firefox pid-je marad a már futó shell pid-je. Erre érvényesül már az ulimit, vagy mindenképp child process kell, mint 2-ben?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

execcel forkolok - na, ilyen allat nincs :) Vagy exec(), vagy fork(), de ez most reszletkerdes. Az ulimit a motorhazteto alatt egy setrlimit() syscall, a beallitott limitek az adott processzre es az abbol inditottakra is ervenyesek, viszont az adott program setrlimit() hivasaval atallithatja az ertekeket egeszen az un. hard limit-ig. Szoval a vezerlo terminal, session leader, stb. beallitasokat leszamitva a ket inditasi mod ugyanaz.

Feliratkozom.

's/Firefox/Thunderbird/i' $subject az én gondom.

Ne nevessetek ki, de azon gondolkodom, ezt akár úgy is lehetne csinálni, hogy unit file-t írni, és systemd service-ként indítani a Firefox-ot. Gondolom, azért, mert valami service, még nem feltétlen root joggal fut, van egy olyan sejtésem, ez is konfigolható egy unit file-ban. Mert, ha jól sejtem, a systemd-ben van resource management, épp a cgroups felhasználásával.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Van még egy bajom. Wrapper script illetve systemd unit sem a legjobb, mert a böngésző hívódhat például xdg-open scripten keresztül. A *.desktop file-okba belenyúlni nem igazán szerencsés a frissítések miatt, hasonló okokból a /usr/bin/firefox átnevezése, majd wrapper script létrehozása sem /usr/bin/firefox néven.

Mivel a PATH-ban a /usr/local/bin előrébb van, mint a /usr/bin, így egy /usr/local/bin/firefox wrapper script létrehozása logikusnak tűnhet, csak senki nem mondta, hogy más módon teljes elérési úttal nem fogja semmi sem indítani a böngészőt, tehát /usr/bin/firefox hivatkozással. Elérési út nélküli hivatkozással ez működne. Csak erre nincs garancia.

Mi erre a problémára a legjobb megfejtés?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Fedorán SELinux van. Viszont systemd szerintem tudja ezt, csak egyelőre nem találtam példákkal jól illusztrált, szájbarágós doksit. Nagyjából, a hogyan írjunk unit file-t című, kezdőknek szóló műre vágyom, ha ez magyarul lenne, még jobb. :)

Szerk.: lehet, valami firefox.slice nevű furcsaságot kellene írnom.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

használj wrappert. cp *.dekstop fájl to valami-enyem.dekstop és magában a fájlban csak az Exec és a Name részt módosítod így az eredit szépen megmarad. Majd azt indítod a menüből vagy bárhonna mert ráismert
nekem most van Firefox és Firefox cgroup alkalmazásom.
--
A legértékesebb idő a pillanat amelyben élsz.
https://sites.google.com/site/jupiter2005ster/

Bocs, nem vagyok szakértő, csak egy tapasztalat.

Van nálad AdBlock Plus? Nekem az igen megnöveli a memóriahasználatot. Sokat segített nekem, amikor azokon az oldalakon, ahol nincs vagy nem zavaró a reklám, kikapcsoltam.

Tudom, hogy a kérdés nem pont ez volt, de talán ez is egy komponens a memóriazabálásban.

Ezt most miért teszi velem?

cgexec -g memory:firefox firefox
console.error: 
  [CustomizableUI]
  Custom widget with id loop-button does not return a valid node
console.error: 
  [CustomizableUI]
  Custom widget with id loop-button does not return a valid node
NOT SANDBOXED

###!!! [Parent][MessageChannel::Call] Error: Channel error: cannot send/recv

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

"Ugyanakkor erős túlzásnak érzem egy böngészőtől, hogy legfeljebb 10 nyitott fül mellett elkérjen több, mint 14 GB memóriát"
Remélem azt tudod, hogy a virtuális memoria nem az igényelt memoria össz mérete

// Happy debugging, suckers
#define true (rand() > 10)

Leírom, hogy mit gondolok róla, aztán legfeljebb kijavítjátok, hol tudom rosszul. Éppen ezért a gúnyolódást, röhögést kérem hanyagolni.

Van az alkalmazás, dinamikusan kér memóriát, talán malloc() függvényt használva. Visszakapja, hogy nesze, itt egy pointer, innentől írhatsz annyit, amennyit kértél. Ha az adott területen belül címez az alkalmazás, kivétel keletkezik, és a kernel egy 4 kB-os (vagy 16 kB-os?) lapot fizikailag is alátesz az alkalmazásnak, meg nyilvántartja valahol, gondolom. Ellenben, ha a foglalt területen kívülre címez, abból SIGSEGV lesz, és a kernel beszántja az alkalmazást, mondván, ne túrkáljon olyan területen, amihez semmi köze.

Aztán, ha már nincs mit alálapozni, amikor a foglalt területen belül címez az alkalmazás, akkor swap-re írja mások területét, törli, majd aláteszi ennek az alkalmazásnak. Swap-el vadul, lassú lesz, de legalább működik. Ha már ez sem segít, az out of memory killer tesz egy becslést, és kinyírja a szerinte memory leak-es process-t, aztán vagy nyert, vagy egy legálisan sokat foglaló folyamatot nyír ki.

Ezt gondolom erről, de lehet benne hiba, javítsatok ki, ha nem így van!

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Teljesen pontosan írtad le, viszont a virtual memory nem csak a megigényelt memóriát tükrözi vissza. A kernel és az allokátorok viszonya bennem sem megingathatatlan információ, de próbálom tömören összefoglalni a bennem lakó infót: A rendszer a virtual memory-t igyekszik egyben tartani hogy ne kelljen a különböző chunkok között váltania. Ezt egyrészt úgy végzi el, hogy a virtual memory kiosztásakor próbálja a lehető legmesszebb pakolni egymástól a processzeket, hogy ezek a chunkok a lehető legkevesebb lépésben legyenek átjárhatóak. A kernel kiosztja a virtuális memóriát és pont, majd ezen pont után jönnek a különböző allokátor megoldások (tcmalloc/jemalloc/glibc allocator, a firefox a jemalloc-ot használja) akik garázdálkothatnak e fölött. A firefox esetében maradjunk a jemalloc-nál, amit pont olyan szoftverekhez találtak ki, akik sűrűn allokálnak és szabadítanak fel memóriát (böngészőnél ez kézenfekvő: html elemek, képek, hangok, js-ek, egyéb include-olt cuccok stb). Az alap, pl glibc allokátor ilyenkor lockol, szütymög, szenved, lassú. A jemalloc-nál ezt úgy oldották meg, hogy a thread-ekhez úgynevezett arena-kat jelölnek ki amikben lock free módon garázdálkodhatnak teljesen. Ennek hozományát nem kell magyaráznom, szimplán baromi gyors, cserébe egészen nagy területek foglal le az arenaknak (ugyanúgy ahogy a kernel is igyekszik egyben tartani a virtuális memóriát). Mivel többszörösen fragmentálódik így memória, viszont nem okoz gondot, senki nem próbálja meg ezt a gyönyörű együtthatást agyonverni, a thread meg majd vissza adja az általa használt arena-t ha befejezte a dolgát.
Szóval ez az oka, hogy miért ilyen baromi nagy a virtuális memória (mindenki fogja, hogy ne szegmentálódjon, cserébe baromi nagy kerek szám lesz a virtuális memória egészen addig míg az allokátor el nem kezdi visszaadni). A jemalloc még úri módon intézi ezt, mert az allokált page-eket valóban visszaadja a kernelnek és csak a virtuális memória részét fogja csak, ezért a valós foglaltság valóban a használt memóriát tükrözi. A tcmalloc tahó módon még a kernelnek sem adja vissza, ül rajta míg vagy megbököd a benne lakó gc-t, vagy ő maga úgy nem dönt hogy erre már valóban nincs szüksége.

Ennek ellenére az 1g foglalt memória valóban nem kevés, de 10 nyitott fűl mellett szerintem ez bőven az elfogadható kategória, a virtual memory pedig gyakorlatilag semmit nem jelent ilyen szempontból.

Ráadásként a cgroups-al csak a valóban foglaltat (jelen esetben az 1g-t fogod kontrollálni), cserébe a cgroups page-enként lefoglal még ha - jól emlékszem - 40byte memóriát a "memory subsystem"-nek

// Happy debugging, suckers
#define true (rand() > 10)

És az a jelenség vajon mi lehet, amikor egy alkalmazás miatt belassul az egész gép, nagyon úgy tűnik, hogy swappel ezerrel és emiatt, de amikor indítok egy topot vagy egy conky-t szinte azonnal rendeződik dolog, és hirtelen normálisan megy minden. Pont azért nyitom meg ezeket, hogy kiderüljön mi kezdett el zabálni, de ilyenkor gyorsan összeszedi magát.
Leginkább az lenne a jó, ha ez az összeszedett állapot meg is maradna, de ezen nem segít ha folyamatosan fut mondjuk a top.
☼☆♫♪♫♪☆☼
AGA@
Fork portal és az egyik logóm :)

használj atop-ot, az fut a háttérben és menti hogy mi történik. Következő lassulás után pedig visszanézheted hogy mégis mit szütymögött a rendszered. Egy ilyen problémát megfogni elég nehéz, ezer meg egy dolgot csinálhat (pl. a kernel éppen a disk cache-t tolja ki, ha szűk a keresztmetszet akkor képes teljesen beállni a gép addig. Ezen egy megfelelően megválasztott scheduler képes segíteni)

// Happy debugging, suckers
#define true (rand() > 10)

Hat, van itt fogalomzavar rendesen a VM kapcsan. A virtualis memoria processzenkent egyedi cimter - igy a processz szemszogebol egyetlen osszefuggo terulet latszik. Nincs semmifele "rendszer" ami a processzeket a virtualis memoria kulonbozo reszeire rakja. Mivel az MMU a hardver resze, igy a kernelnek szinte mindegy, merre vannak az adott processz altal hasznalt fizikai memorialapok - par kivetelt leszamitva, ahol valoban osszefuggo fizikai memoriaterulet kell mert a lapozas nem jatszik (pl. DMA teruletek, kernel stack, hugepage), ill. a NUMA rendszereknel is celszeru lokalis memoriat hasznalni, hogy kozvetlen legyen az elerese, ne kelljen mas node-okon keresztul elerni.

Ennyire mélyen nem akartam bele menni, de igazad van: Az MMU a "rendszer" az én interpretálásomban, az MMU-t meg a kernel vezerli (ráadásként ennek annyi variációja van hogy dunát lehet vele rekeszteni, de ezt a kernel szépen elfedi ezt a processz irányából, meg sok más irányból). Szoval igen, ez így valóban pongyola és a felszínt sem karcolom a VM kapcsán (inkább csak megsimogattam), de nem is akartam vele elmenni a cache-ig. Ez egy fórum hozzászólás a teljesség igénye nélkül, erről egy 100 oldalas doksit lehet írni, nem egy 10 mondatos bejegyzést, ráadásként az allokátor a hozzászólás több mint fele:)

// Happy debugging, suckers
#define true (rand() > 10)

Nem szépen, de gyakorlatilag jól megoldva.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE