[megoldva] java OutOfMemmory

Fórumok

Sziasztok,

Nem nagyon ertek a Java-hoz, de lenne egy kerdesem. Van nehany Java programunk ami kulcs fontossagu szamunkra szoval nem megengedheto semmifele nemtervezett leallas. Mostanaban befelfutottunk tobb OutOfMemmory hibaba ami lenyegeben osszeomlasztotta az alkalmazast. Amit tudni kell, hogy lenyegben adat feldolgozasrol van szo, szoval folyamatosan erkeznek az uj adatok amiket a java interface feldolgoz es kiir. Ennek megfeleloen nincs olyan szituacio, hogy vegleg kifussunk a memoriabol, maximum ideglenese problema lehet.
Ugy en nem vagyok fejleszto, de feltettem azt a kerdest nekik, miert csak egy echo az OutOfMemmory exception miert nem kezeljuk valami ertelmes modon. Erre nagyabol azt a valaszt kaptam, hogy nem ertek a Java-hoz (ami igaz) szoval ne szoljak bele.

-------------------------
Frissites:

Szeretnem felhivni a figyelmet arra, hogy nem megoldat keresek a ceg probremajara itten. Hanem mint nem Java fejleszto felmerult bennem egy kerdes a Java OOM hibakezelesevel kapcsolatban es errol szeretnek velemenyeket hallani.

Szoval szerintem az OOM nem egy vegzetes hiba, csak egy pillanatnyi szituacio. Vegzetesse akkor valik, ha tovabb lepunk es hivatkozunk a le nem foglalt valtozora. Szoval szvsz a crash helyett jobb otletnek tunnik egyszeruen megakassztani a szal futasat addig amig nem tudjuk lefoglalni a valtozot.

Gondoltam valami osztaly letrehozni az alabbi logika alapjan:


	    double[] datalist2;
	    int maxwait = 120000;
	    int currentwait = 1;
	    while (true) {
	    	try {
	    		datalist2 = new double[datalist.length];
	    		break;
	    	} catch (Exception e) {
	    		if (currentwait < maxwait ) {
	    			currentwait *= 2;
                            Thread.sleep(currentwait);
	    		    continue;
	    		} else {
	    			// Something which we want to do if we can't get the allocate the value
	    			//    Ex: kill the threat 
	    			break;
	    		}
	    	}
	    }	

Megprobaltam magam definialni egy osztalyt, de ez nem kepes primitiv tipusokat, es Double es Integer osztalyokat letrehozni:


public class allocateObject  {
    public static <T> T allocateObject (Class clazz) {
    	/* It not work with "java.lang.Integer", "java.lang.Double"
    	 *    as they doesn't have no-arg(default) constructor
    	 */
    	int postpone = 1;    // the actual postpone
    	final int maxpostpone = 1800000;   // the max milliseconds which we trying 
    	
    	
    	T vissza;
    	while(true) {
			try {
				vissza = (T) clazz.newInstance();
			   	return vissza;
			} catch (InstantiationException | IllegalAccessException e) {
				e.printStackTrace();
				if (postpone < maxpostpone ) {
					postpone *= 2;
                                        Thread.sleep(postpone);
					continue;
				} else {
				    return null;
				}
			}
    	}
    }  
}

public class teszt {
	public static void main(String[] args) {
            String teszt2 = allocateObject.allocateObject(String.class);
        }
}

Hozzászólások

1. ha memória leak-ről van szó azt többször sikerült elkapnom úgy hogy láttam jmap-al vagy jprofiler-rel hogy egy saját objektumból gyanúsan több van és növekszik. Nagy mennyiségű adatfeldolgozóknál ez jellemző lehet.

2. ami még sokszor segített: amikor összeomlik a stack-et kiíritva látni lehetett hogy éppen min szöszölt és általában arrafelé is volt a hiba, pl valami olyan input adat ami megakadt a torkán, vagy vmi belső cache rész ahol egyre jobban belassult

3. amikor összeomlik kiírattam az input adatot valami log-ba, sokszor gyanúsan hasonlóak voltak és az vezetett nyomra

Nem nagyon kell vizsgalni: a szokasosnal tobb adat erkezet az adot oraban (6x tobb), ilyesmi megesik. A problema, hogy reszletekben fel lehetne dolgozni (sok kis fajl), de mivel nem varjuk meg amig x fajl-al vegzunk es ujra van memmoria a kovetkezo x-nek, hanem addig olvassuk be az uj adatokat amig OOM nem lessz a vege. Persze lehetne szabalyozni a beolvasott adatmennyiseget, de az en kerdesem inkabb elmeleti: hogy lehet Java-ban addig probakozni a memoria lefoglalasaval amig nem sikerul. Szvsz alapnak tartom, hogy semmit nem leptetunk tovabb ha nem vagyunk biztosak benne, hogy a szugseges valtozok rendelkezesre allnak.

OOM akkor szokott keletkezni, amikor memleak van valahol vagy C/C++ programozók írnak Java programot.

Az első és legfontosabb kérdés: egészen pontosan mekkora a veszteség pénzben kifejezve percenként, amíg nem működik üzemszerűen a szolgáltatás?

Kosszi, de ezek a peldak nagyabol csak annyit csinalnak amit az en elso peldam. Vesznek egy valtozot, aminek __elore__ tudjak a tipusat es addig hivjak amig nem sikerul lefoglalni. Amit en igazabol keresek az egy olyan "general class" ami ugyanezt csinalja. Ugye a ketto kozt az a kulombseg, hogy a "general class"-nal nem tudod a valtozo tipusat, hanem kesobb a objektum-nal addod meg. Ennek az lenne az ertelme, hogy minden egyes valtozo lefoglalasanal egyszeruen lehetne alkalmazni, es a kod is tiszta maradna. Az elso-vel az a gond, hogy sok valtozonal rendesen olvashatatlana tesszi a kodot.

Varargs, amit keresel.

Varargs object tömb, abból megvan a konstruktor paraméterezése (talán, kis trükk lehet kell), majd reflectionnel a konstruktor is.

Attól ez még messze nem a problémát oldja meg, s kifejezetten nem szép, sőt. De, ha nagyon akarod, elméletileg megoldható.
--
blogom

Esetleg keresni kell szakembert, aki ért a JVM-hez, a memóriafoglaláshoz és a többihez... például a legutóbbi JUM-on egészen mély szinten volt szó memória menedzsmentről: http://www.meetup.com/javaforum-hu/events/227497568/

Egyébként ezernyi kérdés felmerül, például, ha ennyire magas rendelkezésre állás kell, akkor miért nem fut több példányban több datacenter-ben? Ez innen nézve több ponton is el van baszva és az nem a memória menedzsment.

--
http://wiki.javaforum.hu/display/~auth.gabor/Home

Lenne-e jó megoldás? Persze, jól megírni a kódot :) Használjatok profilert és keressétek meg hol a gubanc. Nem feltétlen kell memory leaknek lennie ahhoz, hogy elfogyjon a memória.

Mit akar csinálni ez az allocateObject? Itt valami nagyon-nagyon félrement szerintem...

Szerintem OutOfMemoryError-t inkább jobb, ha meg se próbálod elkapni és kezelni, mert csak ront a helyzeten.

--

Miert is? Mindenki ezt szajkozza, de biztosan en vagyok a butta de nem lattom mi lehet rosszabb mint egy crash. Mintha az OOM == "orok karhozat" lenne. Van egy idopillanat, amikor nincs memoria. Ennyi. Hol van az a szabaly, hogy a kovetkezo idopillanatban sem lesz memoria? Mi a baj azzal, ha felfugesztjuk a szall futasat addig amig nem lesz ujra memoria?

nem tudod, mi probal meg memoriat allokalni.
nem tudod, mennyit _nem_ sikerult allokalni. 14 megat? 5 byteot?
az exception is es class instance (==object), ergo minel tobbet kezelgeted a OOM-et, annal tobbet eszel el a maradekbol.
nem lesz a kovetkezo idopillanatban memoria, mert a GC nem fut minden mikroszekundumban.

A mikroszekundumban igazad van, es rajottem, hogy kihagytam a sleep()-et a perdabol. De szerintem azzal, hogy a sleep idotartalmat folyamatosan novelem nagyabol megoldom ezt problemat.

Azt hogy nem tudom mi akar memoriat lefoglalni nagyabol nem igaz. Barmilyen valtozo amit a kesobbiekben hasznalni akarsz muszaly, hogy le legyen foglalva, ezert gondoltam valami altalanos osztalyra ami ezt az ellenorzest es foglalast elvegzi.

Olyan szempontbol igazad van, hogy nem mindegy, hogy az adatfeldolgozas melyik szintjen akadunk meg. Ha a vegen akkor elofordulhat, hogy soha sem tudunk memoriat kiuriteni. Viszont ezt megoldhatot azzal, hogy egy fajl beolvasaskor egybol lefoglalod az egesz teruletet ami az elejetol a vegig szugseges. Ez design kerdes szvsz.

Java alatt vért hugyozol, ha előre le akarsz foglalni mindent. A nyelv úgy lett kitalálva, hogy folyton csak foglalgatsz ezt-azt.

Amúgy a GC úgy működik, hogy ha nincs hely egy ojjektumnak, akkor addig nem jön OOMError, amíg le nem futna egy full-GC. Tehát ha OOM van, azon a várakozás nem segít. Elvben előfordulhat, hogy egy másik szál éppen felszabadítana (mármint elérhetetlenné válik ojjektum, explicit felszabadítás ugye nincs) a következő milliszekundumban, de ennek azért eléggé kicsi az esélye. Erre nem érdemes légvárakat építeni.

Lent leírtam egy hozzászólásban, hogy előre számolni viszont érdemes a meglévő memóriával. Itt le van írva, hogy hogy lehet lekérdezni: http://stackoverflow.com/questions/12807797/java-get-available-memory

"Azt hogy nem tudom mi akar memoriat lefoglalni nagyabol nem igaz."
Vagod a teljes managed keretrendszert? Ne csak a sajat double[] tombodre gondolj. Fingod sincs, mi mit csinal a hatterben.

"hogy egy fajl beolvasaskor egybol lefoglalod az egesz teruletet ami az elejetol a vegig szugseges."
Legyszi ne!!! Pont ezzel szopatod halalra magad! Ha nem kell egyszerre latnod az egesz fajlt, akkor mi a fasznak nyalod fel az egeszet? A levest is kanalankent eszed, nem egyszerre ontod a szadba a labos tartalmat.

Jó, akkor kezdjük ott, hogy fogalmad sincs arról, hogy működik a JVM, és úgy akarsz mindenféle jobbnál jobb megoldást kitalálni. Ezt lehet, csak
nyilván hülyeség. Szóval, minden ilyen esetben a feladat az, hogy megpróbáljuk megérteni a problémát, aztán keresni rá megoldást.

A JVM amikor elindul, akkor lefoglal egy memória címtartományt, amiben majd dolgozni fog. A memóriát még nem használja fel, ennek ellenére lefoglalja a tartományt.
Ezt 64 bites OS-en nagyon szépen lehet látni (ezért is szoktak jönni a vicces kérdések, hogy a jvm tényleg annyi memóriát használ). A JVM a memóriát saját maga kezeli, tehát az alkalmazásfejlesztőknek kell gondoskodni arról, hogy úgy írják meg a programjukat, hogy ebből ne legyen probléma. A probléma egyébként viszonylag adott: van mondjuk 10 file, amit fel kell dolgozni, de csak egyszerre 2, max 3 fér a memóriába. Nos, ha ez a helyzet, akkor tessék szépen egy várakozási sorba pakolni a fileokat, és mindig csak annyit feldolgozni egyszerre, amennyi a memóriából telik, nem pedig elindítani 10 szálat, beolvasni, aztán meg reklamálni, hogy de hát miért nem fér bele.... mondjuk első ötletként.

Lattom mindenki ugyanazt mondja mint a fejlesztoink: "csak noveld a memoriat"
Memmoria szinten ket het alatt 50G-rol eljutottunk 45+64+50G-ig (159G osszesen, most 3 peldanyban futatjuk a programot es az nyers adatokat is szetdobtuk ennek megfeleloen). Szoval lehet novelni a memoriat, de azert ez kezd komikus szintet olteni szerintem. Csak a mihesztartas vegett ennel az egy ugyfelnel fut 32 hasonlo adatfeldolgozas, es az egeszre van 8x200G. Amit lattni kell, hogy ha leallunk akkor az ujrainditasnal, az allas alatt keletkezett adatokat is fel kell dolgozni. Szoval, egy 8 oras allas, 8x terheles jelent a kozvetlenul az ujrainditas utan, ami biztos, hogy hazavag mindent. Masreszt real time rendszernel nem elfogathato szerintem ilyen hibaval leallni.

Amire ra akartam mutatni, hogy a folyamatosan jon kis meretu, de sok nyers adat amiket a feldolgozas utan torlodnek a memoriabol. Szoval ha a program var nehany percet biztos, hogy le tudja foglalni a memoriat.

Persze lehet memoria szivargast keresni stp, de a kerdesem inkabb altalanos: meg lehet Java-ban oldani, hogy csinaljunk egy olyan altalanos osztalyt/fugvenyt ami addig probalja lefoglalni a memoriat amig nem sikerul neki. En szemely szerint ezt egy jogos igenynek erzem. Nem hiszem, hogy normalis barmit tovabbleptetni, amig nem vagyunk biztosak benne, hogy a szugseges valtozok rendelkezere allnak. En szemely szerint minimumnak tartom C/C++-ban a sikeres memoriafoglalas teszteleset, es nem lattom a Java mert lenne mas.

Igen, a java okoszisztema ilyen :D

Az, hogy kurvasok memoriat eszik a progi, azt jelenti, hogy 1) esz nelkul nyaltok fel mindent, 2) koncepcionalis problemak vannak.

"a feldolgozas utan torlodnek a memoriabol"
NEM. a GC "majd egyszer" torli. A hangsuly a "majd"-on van. Ez nem C.

"En szemely szerint minimumnak tartom C/C++-ban a sikeres memoriafoglalas teszteleset, es nem lattom a Java mert lenne mas."
Mert a java az managed: ha nem sikerul foglalni, jon az OOMException. Letesztelte neked.

A managed nyelvek koncepciojaval vannak gondok, nagyon nativ/C++ fejjel gondolkozol, ez mashogy mukodik.

Meg ha mar ennyire sok a adat, raadasul sok kis filebol, akkor Hadoop+Spark.

Egy túrót ilyen. Az agyatlanul megírt program ilyen. Egyre több hasonló problémát látok, akkora a munkaerőhiány, hogy kritikus részeket is kezdenek juniorokkal megíratni, bármiféle kontroll nélkül. Éles üzemben meg jön az "ígyjárás".

Csak hogy példát mondjak, nálunk van egy alkalmazás aminek 200GB szöveges file-t kell feldolgoznia. Az előző verziónak 8 óráig tartott és mondjuk 50-ből egyszer berohadt egy érdekes adatbázis keresztbelockolási probléma miatt.
Az új verzió 15p alatt végez.

A gépben természetesen sose volt 200G memória, ha jól emlékszem a konténernek régen kellett 24G, most 16-t kap de igazából 4-5-nél több sose kell neki.

Én még olyan adatszerkezetet nem láttam aminek egy példány egyszerre memóriában tartása indokolt volna 100 MB-nál többet.
Tehát aki adatfeldolgozó alkalmazáshoz többtíz GB memóriát használ, az valamit borzasztóan benéz.

--
Gábriel Ákos
http://ixenit.com

Sajnos de, ilyen :D elozo munkahelyemen 12-bol 11 programozo ezt ajanlotta, hogy "tegyel ala memoriat meg"... faszom, ahelyett, hogy megjavitanad a szarodat?!

Ne nekem mondd, en tudom, hogy kene, de sok lud disznot gyoz, vergodjenek csak magukban.

Illetve hat mi az, hogy junior meg senior? Csak egy nev amit odabiggyesztenek 2-3 ev utan a poziciodhoz.

Szerintem a többség pont nem azt mondja, hogy növeld a memóriát, hanem a programot kell optimalizálni. Egyrészt ne legyen memory leak, másrészt párhuzamos helyett soros feldolgozás (IO-nak is jót tesz), harmadrészt a teljes világegyetem egyszerre beolvasása helyett a fájloknak egyszerre csak egy kis része legyen memóriában, stb.

--

Nemrég volt egy kalandom egy alkalmazással, ahol több MB-os json-t töltöttek be objektumba a beépített megoldással, hogy abból kiszedjék az adatokat és adatbázisba tegyék. Remek out of memory error-ral köszönte meg a rendszer ezt. A megoldás egy olyan parser használata lett, ami stream-két átadott adatokból szépen folyamatában ki tudta szedni az adatokat. A bazi nagy objektummal 60-80MB-ot evett, a tokeneken végigmászó parserrel viszont 6MB körül maradt a fogyasztás.

Az persze igaz, hogy out of memory-t nem fog meg semmi egy programban.

Ahogy írják a neten elvileg az figyelhető, hogy mennyi memória használható. Szóval, ha nem koppig akarod tolni, hanem okosan, addig, amennyi hely van, úgy talán lehet kezdeni valamit.
http://stackoverflow.com/questions/12807797/java-get-available-memory

Az okosan toltombe igazad van. Nekem ezzel az a problemam, hogy amikor van 100+ alkalmaza, aminel be kell allitani a memoriat, meg a memoria hatart kezd macerassa valni a karbantartas. Ezert merul fel bennem a kerdes nem lenne-e jobb megakasztani egyszeruen a szal futasat addig amig nem tudja lefoglalni a szugseges memoriat? Ha biztos vagyok benne, hogy a idovel lesz memoria csak varni kell es kesz.

Szoval csak ugy erdeklodes szintjen megprobaltam osszehozni egy altalanos osztalyt amivel ez megvalosithato.

Azért itt felmerülnek komoly kérdések ám.

Tegyük fel, hogy elfogyott a memória (pontosabban minden allokálva van már), és van több szálad is, amelyik vár memóriára. Hirtelen felszabadul a memória.
Melyik szálat részesíted előnyben?

Hasonlóképpen:
Tegyük fel, hogy egy szál csak akkor tud elengedni memóriát, ha lefoglalt egy másikat (pl. adatkonverziós művelet -a régi memória kell addig, amíg az újba be nem írtuk az adatokat). Ezt a deadlockot hogyan oldod fel?

Mivel JVM-ben korlátos a memória, amit egy processz használhat, ezért igaz lesz az is nálad, hogy korlátos az egyszerre feldolgozható adatok mennyisége: a JVM nem tud csak úgy a rendszertől még 10 gigát kérni, ha éppen kéne, ez nem C/C++.
Ha tudod, hogy mennyi memóriát adsz a JVM-nek, akkor meg tudod mondani, hogy mennyi adatot tudsz valós időben feldolgozni. Ha ennél többet kéne, mindenképpen kevés lesz a memória. Ez ahhoz vezet (a ti megoldásotokban), hogy mindig lesz szál, ami várakozik (lehet, hogy egy szál órákig csak vár és vár és busy loopban égeti a CPU-t). Nem tudsz ellene mit tenni, a 'várunk, amíg nem tudunk allokálni' ugyanúgy nem megoldás a problémára. Csak a JVM memóriaméretének a beállítása a megoldás.

Az pedig, hogy az alkalmazásotok nem hatékonyan kezel memóriát, azon semmi nem segít, csak a refaktorálás.

Az a probléma, hogy ha out of memory van, az több szál futása esetén egy csomó mindent el tud rontani. Illetve mivel oom van, ezért már magát az exceptiont sem feltétlen lehet allokálni. Meg a loggolást. Meg semmit. És elég valószínűtlen, hogy a követlező utasítás felszabadítana helyet, inkább foglalni akar az is. Stb... A sok gondolkodás eredménye az, hogy a javaslat az lett a Java világban, hogy ez egy nem kezelendő exception, hanem egy mindenképpen abort-hoz vezető error.

De az alapgondolat jó. Lehet tervezni a memóriával. Le tudod kérdezni, hogy mennyi szabad, és aszerint dönthetsz, hogy elindítasz-e egy feldolgozást, vagy várakoztatod. Ezt csinálhatod dinamikusan (futás idejű mérésre alapozva), vagy statikusan előre számítás alapján (X db lehet egyszerre és kész).

A problémát kezelni így lehet:
* Ha valódi memory leak, akkor le kell vadászni (profiler, memory dump, stb) és punktum.
* Esetleg ha tervezhető (feldolgozásonként beragad X MB), akkor időnként újraindítva a szolgáltatást (akár két szerveren felváltva, hogy ne legyen lefedetlen idő) el lehet karistolni a szolgáltatás leállása nélkül. De ezt megcsinálni sem egyszerű, csak vészmegoldásként csinálnám. Bár ha valóban mission critical, hogy ne legyen leállás, akkor amúgy sem ártana egy klaszterezést megcsinálni.
* Ha csak a feldolgozás miatt van pillanatnyi OOM, akkor meg kell becsülni, hogy egy taszk X MB, és Y MB van, akkor N=Y/X taszknál több ne fusson egyszerre. Ezt a legegyszerűbb az alkalmazáson belül megoldani (várakozási sor, és N db feldolgozó), de ha nem lehet az alkalmazáshoz nyúlni, akkor valami előtétet is lehet faragni (Isten ments, de tudjuk, hogy ilyen az élet...)
* Persze amit mások írtak azzal is foglalkozni érdemes: magát a feldolgozást is kellene memóriára optimalizálni, mert valószínűleg "kicsit" pazarló

(Még egy kicsit árnyalja a képet, hogy a Java alatt többféle heap van: menedzselt Java heap és "malloc heap" - azaz a C-ből elérhető heap. És a "malloc heap"-ből is lehet Java alól allokálni (direct memory a Java nevezéktan szerint), de ennek is van egy limitje. Ráadásul a Java alól foglalt Direct memory darabkák garbage collection alatt szabadulnak fel annak ellenére, hogy malloc-cal vannak foglalva a C heapről. Csak hogy egyszerűbb legyen :-)

Kosszi,

Ez egy igazan konstruktiv hozaszolas. Igazabol ezert jo neha hulyesegnek tunno kerdest feltenni, mert egyedul nem feltetlen osszegeztem volna igy a megoldasi lehetosegeket.

Mint tobb helyen hangsulyoztam, nem a ceges problemara keresem a valaszt (erre vannak ezert fizetett kollegak) hanem inkabb altalanos jo gyakorlatot pillanatnyi OOM kezelesere Java-ban. Nagyon ugy tunik uj design szempontokat tanultam ma.

Találkoztam már olyan java programmal, ami a klasszikus Programming Antihero trükköt alkalmazta: induláskor foglalt magának egy tömböt rosszabb időkre, aztán mikor repült az

OutOfMemoryError

, akkor elkapta, törölte a tömbre mutató referenciát, explicit kért egy GC-t, majd próbálta menteni a menthetőt, hátha legalább egy reportot ki tudna préselni magából az addig elvégzett munkáról.

Igen, fontos észlelni, hogy ebből a helyzetből max. ennyi hozható ki: kipréselni egy reportot, hogy wtf van. Ha ugyanis egyszer elfogyott a memória, akkor bármelyik könyvtár belül is hasra eshetett egy allokáción, és a libek jellemzően nem úgy vannak megírva, hogy ezt túléljék, simán otthagyják inkonzisztens állapotban a belüket.

Nyilván lukas a program mint az állat.
Egy sima adatfeldolgozó akárminek nem kellenek tíz-gigabájtok.
Egy elastic is elvan 2GB-tal akármeddig.

Csinálni kell induláskor egy heap dumpot, meg mondjuk egy félóra múlva. Aztán MAT-tal v. akármivel megnézni a diffet.
Na abból kiderül hol a luk.

Memória fragmentációról nem kezdek el magyarázni (h. miért is kell Xms és Xmx -et megadni és ugyanoda tenni)

--
Gábriel Ákos
http://ixenit.com

Az OOM problémát ha már fellépett akkor per def nem lehet JVM-en belül megoldani.
Írhatsz köré egy shell scriptet ami újraindítja ha a java processz megdöglött.

--
Gábriel Ákos
http://ixenit.com

Bocs, nem olvastam a hozzaszolasokat, de kapasbol felmerul a kulonbozo referencia tipusok hasznalata (vagyis a _nem_ hasznalata): http://docs.oracle.com/javase/7/docs/api/java/lang/ref/package-summary…

Tipikusan hosszan futo, sok memoriat hasznalo alkalmazasoknal (pl. cache) kritikus, hogy ne agyatlanul hasznald az alapertelmezett "strong" referenciakat illetve az azokra epulo adattipusokat, hanem - ahol indokolt - ott terj at Weak/Soft/Phantom referenciara (pl. https://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html).

Itt peldaul egy erdekes cikk a temaban: http://www.ibm.com/developerworks/library/j-jtp11225/

Persze nem tudom, hogy ez rajtatok konkretan mennyire segit, illetve nem is feltetelezem, hogy a java fejlesztoitek nem kapasbol ezzel kezdtek a memoriaproblemak utani nyomozast - ezt csupan neked irtam, mint javahoz nem erto, amde erdeklodo egyennek :)

Rég láttam ennél szarabb Java kódot. Talán alkalmazni kéne valakit, aki nem C++ kódot akar írni Java nyelven.

Hogy a OP-beli kerdesre is valaszoljak:

Szoval szerintem az OOM nem egy vegzetes hiba

De, java-ban az. (Sot, mindenhol mashol is:-D) Idezem a doksit:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Tovabbi info OOME-rol itt.

Tehat java-ban az OOM igenis vegzetes, hiszen a futtatokornyezet mindent megtett, hogy memoriat szabaditson fel neked. Mivel o nem tudja, hogypl. egy masik szal 314 millisec mulva elengedne valamit, igy nem kezd el vad feltetelezesekbe bocsatkozni. Nincs tobb memoria? Rabasztal, ennyi. Fejlesztoid optimalizaljanak, vagy vegyel fel kompetens fejlesztoket.

Mi meg a kod nemismereteben nem fogunk tudni tippeket adni.

Miért írod az

OutOfMemory

-t következetesen eggyel több m-mel, mint kellene?

Egy dolog biztos: minden szakértő egyetért abban, hogy nem kell kézileg meghívni a System.gc-t, az automatika mindent megold. A tapasztalat viszont azt sugallja, hogy érdemes rendszeresen meghívni a System.gc()-t. Ez persze nem a szakértők hibája.

Az Error-okat nem kellene elkapni és kezelni, ez nem Exception. A javadoc írja az Errorról, hogy "An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch". Az OOE a VirtualMachineError leszármazottja, ami "Thrown to indicate that the Java Virtual Machine is broken or has run out of resources necessary for it to continue operating". Nem jó feltevés, hogy csak egy pillanatnyi szituáció amit kezelhetnél, Te viszont erre alapozol. Ha mégis kihasználod hogy Throwable és elkapod, és valami logikát akarsz végrehajtani a catch blokkban, akkor lehet hogy csak az ismételt kiváltódását éred el, miközben abban bízol, hogy kezelted.

Másképpen foglalkoznék az OOME-el. Az OOME-ben kell kapnotok magyarázatot is az okra. Először azt kellene megnézni, és az alapján megvizsgálni hogy rosszul van-e megírva a program (pl. leak) vagy rosszul van-e megtervezve (tárkomplexitás szempontjából milyen az algoritmus), esetleg JVM-et kell konfigolni (pl. metaspace méret növelés) de az is lehet, hogy esetleg valós az igény a nagy memóriára.

Ha a nagyobb memóriát nagyobb heappel akarod biztosítani, és nagy maximumot vagy semmit limitet sem adsz meg a heapre, akkor 64 bites OS/JVM esetében érdemes használni a -XX:-UseCompressedOops amivel kikapcsolhatod hogy a jvm 32 bites objektum pointereket (oops) használjon, és 64 bitest fog használni. Ára van, elveszik a 32 bites pointerek használatából adódó teljesítmény előny.

Van arra is lehetőséged, hogy ha kell, akkor a heapen kívül foglaljon helyet nagy méretű adatnak a jvm. Ezt a ByteBuffer.allocateDirect() -el csinálhatod, és nem fogja a heapedet befolyásolni (gc-t sem), ByteBufferként kezelheted. Viszont, ha a JVM-nek beállítod a -XX:MaxDirectMemorySize=mérettel amennyinél nagyobbat ne engedjen foglalni, és akkor ezt is bevonhatod az OutOfMemoryError alá, mert ha nagyobbat allokálnál akkor OOME dobódik. Ha a kész megoldást jobban szereted, akkor in-memory datagrid technológiát lehet használni pl. hazelcast, infinispan, ehcache + terracota bigmemory de ez messzire vezet meg drága is, cserében megéri.