Rekebisha “java.lang.OutOfMemoryError: Java heap space” katika Java: Sababu, Misingi ya Heap, na Suluhisho la Kivitendo

目次

1. Utangulizi

Unapopiga kazi katika Java, je, umewahi kuona programu yako ikikumba ghafla na koni inaonyesha:

java.lang.OutOfMemoryError: Java heap space

Hitilafu hii ina maana “Java imekoma na kumbukumbu inayoweza kutumika (heap).”
Hata hivyo, kutoka ujumbe wa hitilafu pekee, si rahisi kuelewa mara moja:

  • Ni nini kilichosababisha heap kukoma
  • Unapaswa kubadilisha nini, na jinsi
  • Je, tatizo liko katika msimbo au katika usanidi

Kwa sababu hiyo, watu mara nyingi hutegemea “ufumbuzi wa haraka” kama “ongeza tu -Xmx” au “ongeza kumbukumbu zaidi kwenye seva.”

Lakini kuongeza ukubwa wa heap bila kuelewa chanzo kikuu si suluhisho halisi—inaweza pia kusababisha matatizo mengine.

  • GC (ukusanyaji wa takataka) unazidi kuwa mzito na muda wa majibu unadhoofika
  • Kumbukumbu ya jumla ya seva inakuwa ndogo na inaathiri michakato mingine
  • Uvuaji wa kumbukumbu halisi unabaki, na OutOfMemoryError hutokea tena

Ndiyo sababu “java heap space” si tu “kumbukumbu ndogo.”
Unapaswa kuichukulia kama alama ya tatizo la mchanganyiko linalohusisha muundo wa programu, utekelezaji, na mipangilio ya miundombinu.

1-1. Hadhira Inayolengwa

Makala hii imeandaliwa kwa wasomaji ambao:

  • Wanaelewa misingi ya Java (madarasa, mbinu, makusanyo, n.k.)
  • Lakini hawajui kabisa jinsi kumbukumbu inavyosimamiwa ndani ya JVM
  • Wamekutana na “java heap space” au OutOfMemoryError katika maendeleo/majaribio/utengenezaji—au wanataka kuwa tayari
  • Wanaendesha Java kwenye Docker/containers/cloud na wanahisi kidogo wasiwasi kuhusu mipangilio ya kumbukumbu

Miaka yako ya uzoefu na Java hayajali.
Kama unataka “kuelewa hitilafu ipasavyo na kujifunza kuichambua mwenyewe,” mwongozo huu unalenga kuwa wa manufaa moja kwa moja katika kazi halisi.

1-2. Unachojifunza katika Makala Hii

Katika makala hii, tunaelezea hitilafu ya “java heap space” kutoka kwenye mekaniki hadi juu—sio orodha tu ya suluhisho.

Mada kuu ni pamoja na:

  • Nini heap ya Java ni wp:list /wp:list

    • Jinsi inavyotofautiana na stack
    • Mahali ambapo vitu vinatengenezwa
    • Mifumo ya kawaida inayosababisha “java heap space” wp:list /wp:list

    • Upakiaji wa wingi wa data kubwa

    • Makusanyo na caches yanayokua kupita kiasi
    • Uvuaji wa kumbukumbu (msimbo unaobaki na marejeleo hai)
    • Jinsi ya kukagua na kuongeza ukubwa wa heap wp:list /wp:list

    • Chaguzi za mstari wa amri ( -Xms , -Xmx )

    • Mipangilio ya IDE (Eclipse / IntelliJ, n.k.)
    • Vidokezo vya usanidi wa seva ya programu (Tomcat, n.k.)
    • Mbinu za kuokoa kumbukumbu katika msimbo wp:list /wp:list

    • Kurejea jinsi unavyotumia makusanyo

    • Vizingiti vinapotumika streams na lambdas
    • Mikakati ya kugawanya data kubwa
    • Uhusiano kati ya GC na heap wp:list /wp:list

    • Jinsi GC inavyofanya kazi kwa msingi

    • Jinsi ya kusoma log za GC kwa kiwango cha msingi
    • Kugundua uvuaji wa kumbukumbu na kutumia zana wp:list /wp:list

    • Kupata dump ya heap

    • Kuanza na uchambuzi kwa kutumia VisualVM au Eclipse MAT
    • Mambo ya kuzingatia katika mazingira ya kontena (Docker / Kubernetes) wp:list /wp:list

    • Uhusiano kati ya kontena na -Xmx

    • Mipaka ya kumbukumbu kupitia cgroups na OOM Killer

Katika nusu ya pili ya makala, pia tunajibu maswali ya kawaida katika muundo wa FAQ, kama vile:

  • “Je, ninapaswa kuongeza heap tu kwa sasa?”
  • “Ninaweza kuongeza heap hadi wapi kwa usalama?”
  • “Ninawezaje kutambua kwa takriban ikiwa ni uvuaji wa kumbukumbu?”

1-3. Jinsi ya Kusoma Makala Hii

Hitilafu ya “java heap space” ni muhimu kwa watu ambao:

  • Wanahitaji kutatua tukio la uzalishaji sasa hivi
  • Wanataka kuzuia matatizo kabla hayajatokea

Kama unahitaji suluhisho la haraka, unaweza kuruka mbele kwa sehemu za vitendo kama vile:

  • Jinsi ya kubadilisha ukubwa wa heap
  • Jinsi ya kukagua uvuaji wa kumbukumbu

Kwa upande mwingine, kama unataka uelewa kamili, soma kwa mpangilio huu:

  1. Misingi: “Nini heap ya Java”
  2. Sababu za kawaida
  3. Kisha suluhisho na hatua za kurekebisha

Mtiririko huu utakusaidia kuelewa kwa uwazi mekaniki ya hitilafu.

2. Heap ya Java ni Nini?

Kwa kuelewa ipasavyo kosa la “java heap space”, unahitaji kwanza kujua jinsi Java inavyosimamia kumbukumbu. Katika Java, kumbukumbu imegawanywa katika maeneo kadhaa kwa madhumuni, na kati yao heap ina jukumu muhimu kama nafasi ya kumbukumbu kwa vitu.

2-1. Picha Kubwa ya Maeneo ya Kumbukumbu ya Java

Programu za Java zinaendeshwa kwenye JVM (Java Virtual Machine). JVM ina maeneo mengi ya kumbukumbu ili kushughulikia aina tofauti za data. Tatu za kawaida zaidi ni:

■ Aina za Maeneo ya Kumbukumbu

  • Heap Eneo ambapo vitu vilivyotengenezwa na programu huhifadhiwa. Ikiwa hii inakoma, utapata kosa la “java heap space”.
  • Stack Eneo la miito ya mbinu, vigezo vya ndani, marejeo, na mengineyo. Ikiwa hii inajaa, utapata “StackOverflowError.”
  • Method Area / Metaspace Huhifadhi taarifa za darasa, thabiti, metadata, na matokeo ya usakinishaji wa JIT.

Katika Java, vitu vyote vilivyotengenezwa kwa new vinawekwa kwenye heap.

2-2. Jukumu la Heap

Heap ya Java ndiko ambapo vitu kama vifuatavyo huhifadhiwa:

  • Vitu vilivyotengenezwa kwa new
  • Safu (ikiwa ni pamoja na yaliyomo ya List/Map, n.k.)
  • Vitu vinavyotengenezwa ndani na lambdas
  • Mstari (Strings) na vigope vinavyotumika na StringBuilder
  • Miundo ya data inayotumika ndani ya mfumo wa makusanyo

Kwa maneno mengine, wakati Java inahitaji “kuhifadhi kitu katika kumbukumbu,” huwa huhifadhiwa kwenye heap.

2-3. Nini Kinatokea Wakati Heap Inakoma?

Kama heap ni ndogo sana—au programu inatengeneza vitu vingi sana—Java hufanya GC (ukusanyaji wa takataka) ili kurejesha kumbukumbu kwa kuondoa vitu visivyotumika.

Lakini ikiwa GC inayojirudia haijaweza kutoa kumbukumbu ya kutosha, na JVM hawezi tena kugawa kumbukumbu, utapata:

java.lang.OutOfMemoryError: Java heap space

na programu italazimika kusimika.

2-4. “Ongeza Tu Heap” Ni Nusu Sahihi na Nusu Si Sahihi

Kama heap ni ndogo tu, kuiongeza kunaweza kutatua tatizo—kwa mfano:

-Xms1024m -Xmx2048m

Hata hivyo, ikiwa chanzo kikuu ni uvujaji wa kumbukumbu au usindikaji usiofaa wa data kubwa katika msimbo, kuongeza heap kununua tu muda na hakusuluhishi tatizo la msingi.

Kwa kifupi, kuelewa “kwa nini heap inakoma” ndilo jambo muhimu zaidi.

2-5. Mpangilio wa Heap (Eden / Survivor / Old)

Heap ya Java imegawanywa kwa upana katika sehemu mbili:

  • Young generation (vitu vilivyotengenezwa hivi karibuni) wp:list /wp:list

    • Eden
    • Survivor (S0, S1)
    • Old generation (vitu vya muda mrefu)

GC inafanya kazi tofauti kulingana na eneo.

Young generation

Vitu huwekwa kwanza katika Eden, na vitu vya muda mfupi huondolewa haraka. GC hufanyika mara kwa mara hapa, lakini ni nyepesi kidogo.

Old generation

Vitu vinavyodumu kwa muda wa kutosha vinapandishwa kutoka Young kwenda Old. GC katika Old ni ghali zaidi, hivyo ikiwa eneo hili linaendelea kukua, linaweza kusababisha ucheleweshaji au mapumziko.

Katika hali nyingi, kosa la “heap space” hutokea hatimaye kwa sababu Old generation imejaa.

2-6. Kwa Nini Ukosefu wa Heap Ni wa Kawaida kwa Wanaoanza na Wadev wa Kiwango cha Kati

Kwa sababu Java hufanya ukusanyaji wa takataka kiotomatiki, watu mara nyingi wanadhani “JVM inashughulikia usimamizi wote wa kumbukumbu.”

Kwa kweli, kuna njia nyingi za kukoma heap, kama vile:

  • Msimbo unaoendelea kutengeneza idadi kubwa ya vitu
  • Marejeo yanayobaki hai ndani ya makusanyo
  • Streams/lambdas zisizokusudiwa kuzalisha data kubwa
  • Vifungashaji vilivyokua kupita kiasi
  • Kutoelewa mipaka ya heap katika kontena za Docker
  • Usanidi usio sahihi wa heap katika IDE

Ndiyo maana kujifunza jinsi heap inavyofanya kazi ni njia fupi zaidi ya kupata suluhisho la kuaminika.

3. Sababu za Mara kwa Mara za Kosa la “java heap space”

Ukosefu wa heap ni tatizo la mara kwa mara katika mazingira mengi ya ulimwengu halisi, lakini sababu zake zinaweza kugawanywa katika makundi matatu: kiasi cha data, msimbo/mbinu, na usanidi usio sahihi. Katika sehemu hii, tunapanga mifumo ya kawaida na kuelezea kwa nini husababisha kosa.

3-1. Shinikizo la Kumbukumbu kutokana na Kupakia Data Kubwa

.The most common pattern is when the data itself is so large that the heap gets exhausted.

■ Mifano ya Kawaida

  • Kupakia CSV/JSON/XML kubwa mara moja kwenye kumbukumbu
  • Kuchukua idadi kubwa ya rekodi za hifadhidata kwa jaribio moja
  • API ya Wavuti inarudisha jibu kubwa sana (picha, logi, n.k.)

A particularly dangerous scenario is:

Wakati “kamba ghafi kabla ya kuchambua” na “vitu baada ya kuchambua” zipo kwenye kumbukumbu kwa wakati mmoja.

Kwa mfano, ikiwa unapakia JSON ya 500MB kama kamba moja kisha kuisoma kwa Jackson, matumizi ya jumla ya kumbukumbu yanaweza kupita kwa urahisi 1GB.

■ Mwelekeo wa Kupunguza

  • Tambulisha usomaji wa vipande (usindikaji wa mtiririko)
  • Tumia ukabidhi kwa upatikanaji wa hifadhidata
  • Epuka kuweka data ya kati kwa muda mrefu kuliko inavyohitajika

Kufuata kanuni ya “kushughulikia data kubwa kwa vipande” husaidia sana kuzuia heap kuchoka.

3-2. Kuongeza Data Kupita Kiasi katika Makusanyo

Hii ni ya kawaida sana kwa wasanidi wa kiwango cha mwanzo hadi kati.

■ Makosa ya Kawaida

  • Kuongeza daima logi au data ya muda kwenye Listinakua bila kusafishwa
  • Kutumia Map kama hifadhi (ila hauondoi ingizo kamwe)
  • Kujenga vitu vipya daima ndani ya mizunguko
  • Kuzalisha idadi kubwa ya vitu vya muda kupitia Streams au lambdas

Katika Java, mradi rejea ipo, GC haiwezi kuondoa kipengele. Katika hali nyingi, wasanidi kwa bahati mbaya huhifadhi marejea hai.

■ Mwelekeo wa Kupunguza

  • Tambua mzunguko wa maisha kwa hifadhi
  • Weka vizingiti vya uwezo kwa makusanyo
  • Kwa mifumo ya data kubwa, safisha mara kwa mara

Kwa kumbukumbu, hata kama haionekani kama uvujaji wa kumbukumbu:

List<String> list = new ArrayList<>();
for (...) {
    list.add(heavyData);  // ← grows forever
}

Aina hii ya msimbo ni hatari sana.

3-3. Uvuaji wa Kumbukumbu (Ushikaji wa Vitu Visivyo Kutarajiwa)

Kwa sababu Java ina GC, watu mara nyingi wanafikiri “uvuaji wa kumbukumbu haujitokezi katika Java.” Katika vitendo, uvuaji wa kumbukumbu unatokea kabisa katika Java.

■ Sehemu za Mara kwa Mara za Uvuaji

  • Kushikilia vitu katika vigezo vya static
  • Kusahau kuondoa usajili wa wasikilizaji au callbacks
  • Kuweka marejea hai ndani ya Streams/Lambdas
  • Vitu vinavyokusanyika katika kazi za batch zenye muda mrefu
  • Kuhifadhi data kubwa katika ThreadLocal na uzi wa thread unatumika tena

Uvuaji wa kumbukumbu si kitu ambacho unaweza kuepuka kabisa katika Java.

■ Mwelekeo wa Kupunguza

  • Fanyia upya jinsi unavyotumia vigezo vya static
  • Hakikisha removeListener() na close() vinaitwa kila wakati
  • Kwa michakato ya muda mrefu, chukua heap dump na uchunguze
  • Epuka ThreadLocal isipokuwa inahitajika sana

Kwa sababu uvuaji wa kumbukumbu utaendelea hata ukiongeza heap, uchunguzi wa chanzo kikuu ni muhimu.

3-4. Ukubwa wa Heap wa JVM Ni Mdogo Sana (Misingi Ni Midogo)

Wakati mwingine programu inafanya kazi vizuri, lakini heap yenyewe ni ndogo sana.

Ukubwa wa heap chaguomsingi hutofautiana kulingana na OS na toleo la Java. Katika Java 8, kawaida huwekwa takriban 1/64 hadi 1/4 ya kumbukumbu ya kimwili.

Mpangilio hatari unaoonekana mara nyingi katika uzalishaji ni:

No -Xmx specified, while the app processes large data

■ Muktadha wa Kawaida

  • Uzalishaji pekee una kiasi kikubwa cha data, na heap chaguomsingi haitoshi
  • Kuendesha kwenye Docker bila kuweka -Xmx
  • Spring Boot imeanzishwa kama JAR nzito yenye thamani chaguomsingi

■ Mwelekeo wa Kupunguza

  • Weka -Xms na -Xmx kwa thamani zinazofaa
  • Katika kontena, elewa kumbukumbu ya kimwili vs vizingiti vya cgroup na sanidi ipasavyo

3-5. Mifumo ya Muda Mrefu Ambapo Vitu Vinakusanyika

Programu kama ifuatayo huwa hukusanya shinikizo la kumbukumbu kwa muda.

  • Programu za Spring Boot zenye muda mrefu
  • Kazi za batch zinazotumia kumbukumbu nyingi
  • Programu za wavuti zenye trafiki kubwa ya watumiaji

Kazi za batch hasa mara nyingi huonyesha muundo huu:

  • Kumbukumbu inatumika
  • GC inarejesha kidogo tu
  • Baadhi ya mkusanyiko unabaki, na utekelezaji ujao unakutana na OOM

Hii inasababisha makosa mengi ya nafasi ya heap inayoanza kwa kuchelewa.

3-6. Kuelewa vibaya Vikomo katika Vyombo (Docker / Kubernetes)

Kuna tatizo la kawaida katika Docker/Kubernetes:

■ Tatizo

  • Kutoweka -Xmx → Java inarejelea kumbukumbu ya kimwili ya mwenyeji badala ya kikomo cha chombo → inatumia sana → mchakato unauawa na OOM Killer

Hii ni moja ya matukio ya kawaida zaidi ya uzalishaji.

■ Upunguzaji

  • Weka -XX:MaxRAMPercentage kwa usahihi
  • Unganisha -Xmx na kikomo cha kumbukumbu ya chombo
  • Elewa “UseContainerSupport” katika Java 11+

4. Jinsi ya Kuangalia Ukubwa wa Heap

Ukiona kosa la “java heap space”, jambo la kwanza unapaswa kufanya ni thibitisha kiasi gani cha heap kinachotengwa sasa.
Katika hali nyingi, heap ni ndogo kuliko ilivyotarajiwa—kwa hivyo kuangalia ni hatua muhimu ya kwanza.

Katika sehemu hii, tunashughulikia njia za kuangalia ukubwa wa heap kutoka katika mstari wa amri, ndani ya programu, IDEs, na seva za programu.

4-1. Kuangalia Ukubwa wa Heap kutoka Mstari wa Amri

Java inatoa chaguzi kadhaa za kuangalia maadili ya muundo wa JVM wakati wa kuanza.

■ Kutumia -XX:+PrintFlagsFinal

Hii ni njia ya kuaminika zaidi ya kuthibitisha ukubwa wa heap:

java -XX:+PrintFlagsFinal -version | grep HeapSize

Utaona pato kama:

  • InitialHeapSize … ukubwa wa heap wa awali uliotajwa na -Xms
  • MaxHeapSize … ukubwa wa heap wa juu uliotajwa na -Xmx

Mfano:

uintx InitialHeapSize                          = 268435456
uintx MaxHeapSize                              = 4294967296

Hii inamaanisha:

  • Heap ya awali: 256MB
  • Heap ya juu: 4GB

■ Mfano Mahususi

java -Xms512m -Xmx2g -XX:+PrintFlagsFinal -version | grep HeapSize

Hii pia ni muhimu baada ya kubadilisha mipangilio, na inafanya kuwa njia ya kuthibitisha inayotegemewa.

4-2. Kuangalia Ukubwa wa Heap kutoka Ndani ya Programu Inayofanya Kazi

Mara nyingine unataka kuangalia kiasi cha heap kutoka ndani ya programu inayofanya kazi.

Java inafanya hii iwe rahisi kwa kutumia darasa la Runtime:

long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long free = Runtime.getRuntime().freeMemory();

System.out.println("Max Heap:    " + (max / 1024 / 1024) + " MB");
System.out.println("Total Heap:  " + (total / 1024 / 1024) + " MB");
System.out.println("Free Heap:   " + (free / 1024 / 1024) + " MB");
  • maxMemory() … ukubwa wa heap wa juu ( -Xmx )
  • totalMemory() … heap inayotengwa sasa na JVM
  • freeMemory() … nafasi inayopatikana sasa ndani ya heap hiyo

Kwa programu za wavuti au michakato ndefu, kurekodi maadili haya kunaweza kusaidia wakati wa uchunguzi wa tukio.

4-3. Kuangalia Kutumia Zana Kama VisualVM au Mission Control

Unaweza pia kukagua matumizi ya heap kwa kuona na zana za GUI.

■ VisualVM

  • Onyesho la wakati halisi la matumizi ya heap
  • Muda wa GC
  • Kukamata dump ya heap

Ni zana ya kawaida, inayotumiwa sana katika maendeleo ya Java.

■ Java Mission Control (JMC)

  • Inaruhusu uchambuzi wa kina zaidi
  • Inafaa sana kwa shughuli kwenye Java 11+

Zana hizi zinakusaidia kuona matatizo kama kuzalishwa kwa Old pekee.

4-4. Kuangalia katika IDEs (Eclipse / IntelliJ)

Ikiwa unaendesha programu yako kutoka IDE, mipangilio ya IDE inaweza kuathiri ukubwa wa heap.

■ Eclipse

Window → Preferences → Java → Installed JREs

Au weka -Xms / -Xmx chini ya:
Run Configuration → VM arguments

■ IntelliJ IDEA

Help → Change Memory Settings

Au ongeza -Xmx chini ya chaguzi za VM katika Run/Debug Configuration.

Kuwa mwangalifu—mara nyingine IDE yenyewe inaweka kikomo cha heap.

4-5. Kuangalia katika Seva za Programu (Tomcat / Jetty)

Kwa programu za wavuti, ukubwa wa heap mara nyingi hutajwa katika hati za kuanza za seva.

■ Mfano wa Tomcat (Linux)

CATALINA_OPTS="-Xms512m -Xmx2g"

■ Mfano wa Tomcat (Windows)

set JAVA_OPTS=-Xms512m -Xmx2g

Katika uzalishaji, kuacha hii kwenye chaguo-msingi ni jambo la kawaida—na mara nyingi husababisha makosa ya nafasi ya heap baada ya huduma kuendesha kwa muda.

4-6. Kukagua Heap katika Docker / Kubernetes (Muhimu)

Katika kontena, kumbukumbu ya kimwili, cgroups, na mipangilio ya Java vinaingiliana kwa njia ngumu.

Katika Java 11+, “UseContainerSupport” inaweza kurekebisha heap kiotomatiki, lakini tabia bado inaweza kuwa isiyotarajiwa kulingana na:

  • Kikomo cha kumbukumbu ya kontena (kwa mfano, --memory=512m )
  • Iwapo -Xmx imewekwa wazi

Kwa mfano, ikiwa umeweka tu kikomo cha kumbukumbu ya kontena:

docker run --memory=512m ...

na usiwe na -Xmx, unaweza kukutana na:

  • Java inarejelea kumbukumbu ya mwenyeji na kujaribu kugawa sana
  • cgroups inatekeleza kikomo
  • Mchakato hushtuliwa na OOM Killer

Hii ni tatizo la uzalishaji linalojulikana sana.

4-7. Muhtasari: Kukagua Heap Ni Hatua ya Kwanza ya Lazima

Ukosefu wa heap unahitaji suluhisho tofauti sana kulingana na chanzo. Anza kwa kuelewa, kama seti:

  • Ukubwa wa heap wa sasa
  • Matumizi halisi
  • Uwasilishaji kupitia zana

5. Suluhisho #1: Kuongeza Ukubwa wa Heap

Jibu la moja kwa moja kwa kosa la “java heap space” ni kuongeza ukubwa wa heap. Ikiwa chanzo ni ukosefu rahisi wa kumbukumbu, kuongeza heap ipasavyo kunaweza kurejesha tabia ya kawaida.

Hata hivyo, unapoongeza heap, ni muhimu kuelewa mbinu sahihi za usanidi na tahadhari muhimu. Mipangilio isiyo sahihi inaweza kusababisha kupungua kwa utendaji au masuala mengine ya OOM (Out Of Memory).

5-1. Kuongeza Ukubwa wa Heap Kutoka kwa Mstari wa Amri

Ukianza programu ya Java kama JAR, njia ya msingi zaidi ni kubainisha -Xms na -Xmx:

■ Mfano: 512MB ya Awali, Max 2GB

java -Xms512m -Xmx2g -jar app.jar
  • -Xms … ukubwa wa heap wa awali unaohifadhiwa wakati wa kuanzisha JVM
  • -Xmx … ukubwa wa juu wa heap ambao JVM inaweza kutumia

Katika hali nyingi, kuweka -Xms na -Xmx kwa thamani sawa husaidia kupunguza mzigo wa kubadilisha ukubwa wa heap.

Example:

java -Xms2g -Xmx2g -jar app.jar

5-2. Usanidi kwa Programu za Seva za Maziwa (Tomcat / Jetty, nk.)

Kwa programu za wavuti, weka chaguo hizi katika maandishi ya kuanzisha seva ya programu.

■ Tomcat (Linux)

Set in setenv.sh:

export CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx2048m"

■ Tomcat (Windows)

Set in setenv.bat:

set CATALINA_OPTS=-Xms512m -Xmx2048m

■ Jetty

Add the following to start.ini or jetty.conf:

--exec
-Xms512m
-Xmx2048m

Kwa sababu programu za wavuti zinaweza kupanda kwa matumizi ya kumbukumbu kulingana na trafiki, uzalishaji kwa ujumla unapaswa kuwa na nafasi zaidi kuliko mazingira ya majaribio.

5-3. Mipangilio ya Heap kwa Programu za Spring Boot

Ukikimbia Spring Boot kama JAR nzito, misingi ni sawa:

java -Xms1g -Xmx2g -jar spring-app.jar

Spring Boot huwa hutumia kumbukumbu zaidi kuliko programu rahisi ya Java kwa sababu inapakia darasa nyingi na usanidi wakati wa kuanzisha.

Mara nyingi hutumia kumbukumbu zaidi kuliko programu ya Java ya kawaida.

5-4. Mipangilio ya Heap katika Docker / Kubernetes (Muhimu)

Kwa Java katika kontena, lazima uwe mwangalifu kwa sababu vikwazo vya kontena na hesabu ya heap ya JVM vinaingiliana.

■ Mfano wa Kupendekezwa (Docker)

docker run --memory=1g \
  -e JAVA_OPTS="-Xms512m -Xmx800m" \
  my-java-app

■ Kwa Nini Lazima Uweke -Xmx Kwa Uwazi

Ikiwa hutaeleza -Xmx katika Docker:

  • JVM inaamua ukubwa wa heap kulingana na kumbukumbu ya kimwili ya mashine mwenyeji, si kontena
  • Inaweza kujaribu kugawa kumbukumbu zaidi kuliko inavyoruhusiwa na kontena
  • Inavuka kikomo cha kumbukumbu cha cgroup na mchakato hushtuliwa na OOM Killer

Kwa sababu hii ni tatizo la uzalishaji linalojulikana sana, unapaswa kila wakati kuweka -Xmx katika mazingira ya kontena.

5-5. Mifano ya Mipangilio ya Heap kwa Mazingira ya CI/CD na Wingu

Katika mazingira ya Java yanayotegemea wingu, kanuni ya kawaida ni kuweka heap kulingana na kumbukumbu inayopatikana:

Total MemoryRecommended Heap (Approx.)
1GB512–800MB
2GB1.2–1.6GB
4GB2–3GB
8GB4–6GB

※ Acha kumbukumbu iliyobaki kwa OS, mzigo wa GC, na viti vya nyuzi.

Katika mazingira ya wingu, kumbukumbu jumla inaweza kuwa ndogo. Ikiwa utaongeza heap bila mipango, programu nzima inaweza kuwa isiyotulika.

5-6. Je, Kuongeza Heap Daima Husaidia? → Kuna Mipaka

Kuongeza ukubwa wa heap kunaweza kuondoa kosa kwa muda, lakini hakitatui hali kama:

  • Kuna uvujaji wa kumbukumbu
  • Mkusanyiko unaendelea kukua milele
  • Data kubwa inashughulikiwa kwa wingi
  • Programu ina muundo usio sahihi

Hivyo chukua kuongeza heap kama kifaa cha dharura, na hakikisha unafuata na ubora wa msimbo na kurejea muundo wa usindikaji wa data, ambayo tutazungumzia baadaye.

6. Suluhisho #2: Boresha Msimbo Wako

Kuongeza ukubwa wa heap kunaweza kuwa suluhisho la madhubuti, lakini ikiwa chanzo kikuu kiko katika muundo wa msimbo wako au njia unavyoshughulikia data, kosa la “java heap space” litarejea muda mfupi au mrefu.

Katika sehemu hii, tutashughulikia mifumo ya kawaida ya usimbaji inayotumika katika dunia halisi ambayo hutumia kumbukumbu kupita kiasi, na mbinu thabiti za kuiboresha.

6-1. Fikiria Upya Jinsi Unavyotumia Mikusanyiko

Java collections (List, Map, Set, n.k.) ni rahisi kutumia, lakini matumizi yasiyojali yanaweza kuwa chanzo kikuu cha ukuaji wa kumbukumbu.

■ Muundo ①: List / Map Inakua Bila Kikomo

Mfano wa kawaida:

List<String> logs = new ArrayList<>();

while (true) {
    logs.add(fetchLog());   // ← grows forever
}

Mikusanyiko yenye hakuna hali ya mwisho wazi au kikomo cha juu itashinikiza heap kwa uaminifu katika mazingira ya muda mrefu.

● Maboresho
  • Tumia mkusanyiko wenye kikomo (kwa mfano, weka ukubwa wa juu na futa ingizo za zamani)
  • Safisha mara kwa mara thamani ambazo hauzihitaji tena
  • Ikiwa unatumia Map kama kashe, tumia kashe yenye utakaso → Guava Cache au Caffeine ni chaguo nzuri

■ Muundo ②: Kutoweka Uwezo wa Awali

ArrayList na HashMap huongezeka kiotomatiki wanapozidi uwezo, lakini ukuaji huo unahusisha:
kugawa safu mpya → kunakili → kutupa safu ya zamani.

Unaposhughulikia seti kubwa za data, kutoweka uwezo wa awali si bora na inaweza kupoteza kumbukumbu.

● Mfano wa Maboresho
List<String> items = new ArrayList<>(10000);

Kama unaweza kutabiri ukubwa, ni bora kuuweka mapema.

6-2. Epuka Usindikaji wa Wingi wa Data Kubwa (Shughulikia Kwa Sehemu)

Ukishughulikia data kubwa mara moja, ni rahisi kuingia katika hali mbaya zaidi:
kila kitu kinakamilika kwenye heap → OOM.

■ Mfano Mbaya (Soma Faili Kubwa Mara Moja)

String json = Files.readString(Paths.get("large.json"));
Object data = new ObjectMapper().readValue(json, Data.class);

■ Maboresho

  • Tumia usindikaji wa mtiririko (kwa mfano, Jackson Streaming API)
  • Soma katika sehemu ndogo (kurasa za batch)
  • Shughulikia mitiririko kwa mfululizo na usihifadhi seti nzima ya data
● Mfano: Shughulikia JSON Kubwa kwa Jackson Streaming
JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
    while (!parser.isClosed()) {
        JsonToken token = parser.nextToken();
        // Perform only what you need, and do not retain it in memory
    }
}

6-3. Epuka Uundaji wa Viumbe Visivyo na Lazima

Mtiririko na lambdas ni rahisi kutumia, lakini yanaweza kuzalisha idadi kubwa ya viumbe vya muda ndani.

■ Mfano Mbaya (Kujenga Orodha Kubwa ya Kipatili kwa Streams)

List<Result> results = items.stream()
        .map(this::toResult)
        .collect(Collectors.toList());

Kama items ni kubwa, idadi kubwa ya viumbe vya muda hutengenezwa, na heap inapanuka.

● Maboresho
  • Shughulikia kwa mfululizo kwa kutumia for loop
  • Andika tu kile unachohitaji mara moja (usihifadhi kila kitu)
  • Epuka collect(), au udhibiti mwenyewe

6-4. Kuwa Makini na Kuunganisha String

■ Maboresho

  • Tumia StringBuilder kwa uunganishaji mzito
  • Epuka uunganishaji usio wa lazima unapozalisha logi
    StringBuilder sb = new StringBuilder();
    for (String s : items) {
        sb.append(s);
    }
    

6-5. Usijenge Hifadhi Muda Kupita Kiasi

Hii ni hali ya kawaida katika programu za wavuti na usindikaji wa batch:

  • “Tumeongeza hifadhi muda kwa ajili ya kasi.”
  • → lakini tulisahau kuifuta
  • → hifadhi muda inaendelea kukua
  • → upungufu wa heap → OOM

■ Maboresho

  • Weka TTL (muda wa kumalizika kulingana na wakati) na ukubwa wa juu zaidi
  • Kutumia ConcurrentHashMap kama mbadala wa hifadhi muda ni hatari
  • Tumia hifadhi muda iliyosimamiwa vizuri kama Caffeine inayodhibiti kumbukumbu ipasavyo

6-6. Usijenge Upya Viumbe Ndani ya Mizunguko Mikubwa

■ Mfano Mbaya

for (...) {
    StringBuilder sb = new StringBuilder(); // created every iteration
    ...
}

Hii inaunda viumbe vya muda zaidi kuliko inavyohitajika.

● Uboreshaji
StringBuilder sb = new StringBuilder(); 
for (...) {
    sb.setLength(0);  // reuse
}

6-7. Gawanya Kazi Inayohitaji Kumbukumbu Kubwa katika Mchakato Tofauti

Unaposhughulikia data kubwa sana katika Java, unaweza kuhitaji kutazama upya usanifu wa usanidi.

  • Tenganisha ETL katika kazi ya batch iliyojitolea
  • Toa jukumu kwa usindikaji ulio sambamba (Spark au Hadoop)
  • Gawanya huduma ili kuepuka ushindani wa heap

6-8. Uboreshaji wa Msimbo Ni Hatua Muhimu Ili Kuzuia Kurudi

Ukiongeza heap tu, hatimaye utaifikia “kikomo” kinachofuata, na kosa lile lile litatokea tena.

Ili kuzuia kabisa makosa ya “java heap space”, lazima ufanye:

  • Elewa wingi wa data yako
  • Pitia mifumo ya uundaji wa viumbe
  • Boresha muundo wa makusanyo

7. Suluhisho #3: Rekebisha GC (Uchujaji Takataka)

Kosa la “java heap space” linaweza kutokea si tu wakati heap ni ndogo, bali pia wakati GC haiwezi kurejesha kumbukumbu kwa ufanisi na heap inavyopungua polepole hadi kujazwa.

Bila kuelewa GC, unaweza kurasimu kwa urahisi dalili kama:
“Kumbukumbu inapaswa kupatikana, lakini bado tunapata makosa,” au “Mfumo unakuwa polepole sana.”

Sehemu hii inaelezea taratibu za msingi za GC katika Java na vidokezo vya urekebishaji vinavyosaidia katika shughuli halisi.

7-1. GC ni Nini (Uchujaji Takataka)?

GC ni mfumo wa Java wa kutupa kiotomatiki viumbe ambavyo havihitajwi tena.
Heap ya Java imegawanywa kwa ujumla katika vizazi viwili, na GC inafanya kazi tofauti katika kila kizazi.

● Kizazi cha Vijana (viumbe vya muda mfupi)

  • Eden / Survivor (S0, S1)
  • Data ya muda inayoundwa ndani, n.k.
  • GC hutokea mara kwa mara, lakini ni nyepesi

● Kizazi cha Wazee (viumbe vya muda mrefu)

  • Viumbe vinavyopandishwa kutoka Vijana
  • GC ni nzito; ikiwa hutokea mara kwa mara, programu inaweza “kuganda”

Katika hali nyingi, “java heap space” hatimaye hutokea wakati kizazi cha Wazee kinajaa.

7-2. Aina za GC na Sifa (Jinsi ya Kuchagua)

Java inatoa algoriti nyingi za GC.
Kuchagua sahihi kulingana na mzigo wako wa kazi kunaweza kuboresha utendaji kwa kiasi kikubwa.

● ① G1GC (Chaguo-msingi tangu Java 9)

  • Inagawanya heap katika maeneo madogo na kuyarejesha kidogo kidogo
  • Inaweza kuweka mapumziko ya “stop-the-world” mafupi
  • Inafaa sana kwa programu za wavuti na mifumo ya biashara

→ Kwa ujumla, G1GC ni chaguo-msingi salama

● ② Parallel GC (Nzuri kwa kazi za batch zenye mtiririko mkubwa)

  • Inafanya kazi kwa usambazaji na haraka
  • Lakini muda wa mapumziko unaweza kuwa mrefu
  • Mara nyingi ni faida kwa usindikaji wa batch unaohitaji CPU nyingi

● ③ ZGC (GC ya latency ndogo yenye mapumziko ya milisekunde)

  • Inapatikana katika Java 11+
  • Kwa programu zinazohitaji latency ndogo (vifaa vya michezo, HFT)
  • Inafanya kazi vizuri hata na heap kubwa (tens ya GB)

● ④ Shenandoah (GC ya latency ndogo)

  • Mara nyingi inahusishwa na usambazaji wa Red Hat
  • Inaweza kupunguza mapumziko kwa haraka
  • Pia inapatikana katika baadhi ya matoleo kama AWS Corretto

7-3. Jinsi ya Kubadili GC Kwa Uwazi

G1GC ni chaguo-msingi katika usanidi mwingi, lakini unaweza kubainisha algoriti ya GC kulingana na lengo lako:

# G1GC
java -XX:+UseG1GC -jar app.jar

# Parallel GC
java -XX:+UseParallelGC -jar app.jar

# ZGC
java -XX:+UseZGC -jar app.jar

Kwa sababu algoriti ya GC inaweza kubadilisha tabia ya heap kwa kasi na wakati wa kusimama, mifumo ya uzalishaji mara nyingi huweka hii wazi.

7-4. Toa Logi za GC na Uchunguzi wa Tatizo Kwa Macho

Ni muhimu kuelewa kiasi gani kumbukumbu GC inarudisha na mara ngapi kusimama kwa-dunia-kote hutokea.

● Mipangilio ya Msingi ya Kurekodi GC

java \
  -Xms1g -Xmx1g \
  -XX:+PrintGCDetails \
  -XX:+PrintGCDateStamps \
  -Xloggc:gc.log \
  -jar app.jar

Kwa kuchunguza gc.log, unaweza kutambua dalili wazi za shinikizo la heap, kama vile:

  • Young GC nyingi sana
  • Kizazi cha Old hakipungui kamwe
  • Full GC hutokea mara kwa mara
  • Kila GC inarudisha kiasi kidogo isiyo ya kawaida

7-5. Hali Ambapo Latency ya GC Inachochea “java heap space”

Ikiwa shinikizo la heap linasababishwa na mifumo kama ifuatayo, tabia ya GC inakuwa kidokezo muhimu cha maamuzi.

● Dalili

  • Programu inaganda ghafla
  • GC inaendesha kwa sekunde hadi makumi ya sekunde
  • Kizazi cha Old kinaendelea kukua
  • Full GC huongezeka, na hatimaye OOM hutokea

Hii inaonyesha hali ambapo GC inajaribu kwa bidii, lakini haiwezi kurudisha kumbukumbu ya kutosha kabla ya kugonga kikomo.

■ Sababu za Kawaida za Msingi

  • Uvujaji wa kumbukumbu
  • Mikusanyiko inayohifadhiwa milele
  • Vituo vinavyoishi muda mrefu sana
  • Uvimbe wa kizazi cha Old

Katika hali hizi, kuchanganua logi za GC kunaweza kukusaidia kubainisha ishara za uvujaji au ongezeko la mzigo wakati maalum.

7-6. Mambo Muhimu Wakati wa Kurekebisha G1GC

G1GC ni yenye nguvu kwa chaguo-msingi, lakini kurekebisha kunaweza kuifanya iwe thabiti zaidi.

● Vigezo vya Kawaida

-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=8m
-XX:InitiatingHeapOccupancyPercent=45
  • MaxGCPauseMillis → Wakati wa lengo la kusimama (k.m., 200ms)
  • G1HeapRegionSize → Ukubwa wa eneo linalotumiwa kugawanya heap
  • InitiatingHeapOccupancyPercent → Asilimia ya uzani wa Old-gen inayochochea mzunguko wa GC

Hata hivyo, katika hali nyingi chaguo-msingi ni sawa, kwa hivyo badilisha hizi tu wakati una hitaji wazi.

7-7. Muhtasari wa Kurekebisha GC

Maboresho ya GC yanakusaidia kuona vipengele ambavyo si dhahiri kutoka kwa kuongeza tu ukubwa wa heap:

  • Maisha ya kitu
  • Mifumo ya matumizi ya mikusanyiko
  • Kama uvujaji wa kumbukumbu upo
  • Mahali shinikizo la heap linakusanyika

Ndiyo sababu kurekebisha GC ni mchakato muhimu sana kwa kupunguza “java heap space”.

8. Suluhisho #4: Tambua Uvujaji wa Kumbukumbu

Ikiwa kosa bado linajirudia hata baada ya kuongeza heap na kuboresha msimbo, mshukiwa uwezekanifu zaidi ni uvujaji wa kumbukumbu.

Watu mara nyingi hudhani Java inastahimili uvujaji wa kumbukumbu kwa sababu GC ipo, lakini katika mazoezi, uvujaji wa kumbukumbu ni moja ya sababu zenye shida zaidi na zinazoweza kurudi tena katika mazingira halisi.

Hapa, tunazingatia hatua za vitendo ambazo unaweza kutumia mara moja, kutoka kuelewa uvujaji hadi kutumia zana za uchambuzi kama VisualVM na Eclipse MAT.

8-1. Je, Ni Nini Uvujaji wa Kumbukumbu? (Ndio, Hutokea katika Java)

Uvujaji wa kumbukumbu wa Java ni:

Hali ambapo marejeleo ya vitu visivyo vya lazima vinabaki, vinazuia GC kurudisha.

Hata na kukusanya takataka, uvujaji hutokea kwa kawaida wakati:

  • Vitu vinahifadhiwa katika nyanja za static
  • Msikilizaji waliosajiliwa kwa nguvu hawasajiliwi tena kamwe
  • Mikusanyiko inaendelea kukua na inahifadhi marejeleo
  • Thamani za ThreadLocal zinaendelea bila kutarajiwa
  • Maisha ya mfumo hayaendani na maisha ya kitu chako

Kwa hivyo uvujaji ni uwezekano wa kawaida kabisa.

8-2. Mifumo ya Kawaida ya Uvujaji wa Kumbukumbu

● ① Kukua kwa Mikusanyiko (Kawaida Zaidi)

Kuongeza kwa mara kwa mara kwenye List/Map/Set bila kuondoa viingilio.
Katika mifumo ya biashara ya Java, sehemu kubwa ya matukio ya OOM hutoka kwa mfumo huu.

● ② Kushikilia Vitu katika Vigezo vya static

private static List&lt;User&gt; cache = new ArrayList&lt;&gt;();

Hii mara nyingi huwa ni mahali pa kuanza pa uvujaji.

● ③ Kusahau Kusajili Tena Msikilizaji / Callbacks

Marejeleo hubaki nyuma kupitia GUI, wachunguzi, msikilizaji wa matukio, n.k.

● ④ Kutumia vibaya ThreadLocal

Katika mazingira ya thread-pool, thamani za ThreadLocal zinaweza kuendelea muda mrefu zaidi kuliko ilivyokusudiwa.

.### ● ⑤ Marejeleo Yanayobakiwa na Maktaba za Nje

Baadhi ya “kumbukumbu iliyofichwa” ni vigumu kudhibiti kutoka kwa msimbo wa programu, na kufanya uchambuzi unaotegemea zana kuwa muhimu.

8-3. Vidokezo vya Kugundua “Alama” za Uvuaji wa Kumbukumbu

Ukiona yafuatayo, unapaswa kudhania sana uvuaji wa kumbukumbu:

  • Kizazi cha Old kinaongezeka kwa uthabiti tu
  • GC kamili inakuwa ya mara kwa mara
  • Kumbukumbu haipungui vibaya hata baada ya GC kamili
  • Matumizi ya heap yanaongezeka kadiri muda wa uendeshaji unavyoongezeka
  • Mazingira ya uzalishaji yanavunjika tu baada ya muda mrefu wa uendeshaji

Hizi ni rahisi kuelewa zaidi mara tu zitakapochorwa kwa zana.

8-4. Zana #1: Kagua Uvuaji kwa Kielelezo kwa VisualVM

VisualVM mara nyingi huambatanishwa na JDK katika baadhi ya usanidi na ni rahisi kutumia kama zana ya kwanza.

● Unachoweza Kufanya na VisualVM

  • Ufuatiliaji wa wakati halisi wa matumizi ya kumbukumbu
  • Thibitisha ukuaji wa kizazi cha Old
  • Mara kwa mara ya GC
  • Ufuatiliaji wa nyuzi
  • Kukamata dump za heap

● Jinsi ya Kukamata Dump ya Heap

Katika VisualVM, fungua kichupo cha “Monitor” na ubofye kitufe cha “Heap Dump”.

Unaweza kisha kupitisha dump ya heap iliyokamatwa moja kwa moja kwenye Eclipse MAT kwa uchambuzi wa kina.

8-5. Zana #2: Uchambuzi wa Kina kwa Eclipse MAT (Memory Analyzer Tool)

Kama kuna zana moja ya viwango vya tasnia kwa uchambuzi wa uvuaji wa kumbukumbu wa Java, ni Eclipse MAT.

● Kinachoweza Kuonyesha MAT

  • Vitu vipi vinavyotumia kumbukumbu nyingi zaidi
  • Njia gani za rejea zinazoifanya vitu viishi
  • Kwa nini vitu havitolewa
  • Ukunjufu wa makusanyo
  • Ripoti za “Leak Suspects” kiotomatiki

● Hatua za Msingi za Uchambuzi

  1. Fungua dump ya heap (*.hprof)
  2. Endesha “Leak Suspects Report”
  3. Pata makusanyo yanayoshikilia kiasi kikubwa cha kumbukumbu
  4. Angalia Dominator Tree ili kutambua vitu “vya mzazi”
  5. Fuata njia ya rejea (“Path to GC Root”)

8-6. Ukijua Dominator Tree, Uchambuzi Unakua Haraka Sana

Dominator Tree hukusaidia kutambua vitu vinavyodominia (kudhibiti) sehemu kubwa za matumizi ya kumbukumbu.

Mifano ni pamoja na:

  • ArrayList kubwa
  • HashMap yenye idadi kubwa ya funguo
  • Kache ambayo haijatolewa kamwe
  • Singleton iliyoshikiliwa na static

Kuvipata hivi kunaweza kupunguza kwa kiasi kikubwa muda wa kugundua uvuaji.

8-7. Jinsi ya Kukamata Dump ya Heap (Mstari wa Amri)

Unaweza pia kukamata dump ya heap kwa kutumia jmap:

jmap -dump:format=b,file=heap.hprof <PID>

Unaweza pia kusanidi JVM ili itoe dump ya heap kiotomatiki wakati OOM itatokea:

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump.hprof

Hii ni muhimu kwa uchunguzi wa matukio ya uzalishaji.

8-8. Utatuzi Halisi wa Uvuaji Unahitaji Mabadiliko ya Msimbo

Kama uvuaji upo, hatua kama:

  • Kuongeza ukubwa wa heap
  • Kurekebisha GC

ni hatua za muda mfupi za msaada wa maisha.

Hatimaye, unahitaji mabadiliko ya muundo kama:

  • Kurekebisha sehemu inayoshikilia rejea bila kikomo
  • Kukagua upya muundo wa makusanyo
  • Kuepuka matumizi ya kupita kiasi ya static
  • Kutekeleza utoaji wa kache na usafi

8-9. Jinsi ya Kutofautisha “Ukosefu wa Heap” vs “Uvuaji wa Kumbukumbu”

● Katika Kesi ya Ukosefu wa Heap

  • OOM hutokea haraka kadiri kiasi cha data kinavyoongezeka
  • Inakua kulingana na mzigo wa kazi
  • Kuongeza heap kunasawazisha mfumo

● Katika Kesi ya Uvuaji wa Kumbukumbu

  • OOM hutokea baada ya muda mrefu wa uendeshaji
  • Kadiri maombi yanavyoongezeka, utendaji unadhoofika polepole
  • Kumbukumbu haipungui vibaya hata baada ya GC kamili
  • Kuongeza heap hakuiwezeshi kutatua

8-10. Muhtasari: Ikiwa Marekebisho ya Heap Hayatatui OOM, Shuangia Uvuaji

Kati ya masuala ya “java heap space”, chanzo cha msingi kinachochukua muda mwingi kugundua ni mara nyingi uvuaji wa kumbukumbu.

Lakini kwa VisualVM + Eclipse MAT, ni kawaida kugundua ndani ya dakika:

  • Vitu vinavyotumia kumbukumbu nyingi zaidi
  • Marejeo ya mizizi yanayowafanya viishi
  • Chanzo cha ukunjufu wa makusanyo

9. Masuala ya “java heap space” katika Docker / Kubernetes na Jinsi ya Kuyatatua

.Modern Java applications increasingly run not only on on‑prem environments but also on Docker and Kubernetes (K8s).
However, because container environments use a different memory model than the host, there are many easy‑to‑misunderstand points for Java developers, and “java heap space” errors or OOMKilled (forced container termination) can happen frequently.

This section summarizes container‑specific memory management and the settings you must know in real operations.

9-1. Kwa Nini Makosa ya Nafasi ya Heap Yanajulikana Sana katika Kontena

The reason is simple:

Java huenda isiweze kutambua kikamilifu mipaka ya kumbukumbu ya kontena.

● A Common Misconception

“Since I set a Docker memory limit --memory=512m, Java should run within 512MB.”

→ In practice, that assumption can be wrong.

When deciding heap size, Java may reference the host’s physical memory rather than the container’s limits.

As a result:

  • Java decides “the host has plenty of memory”
  • It attempts to allocate a larger heap
  • Once it exceeds the container limit, the OOM Killer runs and the process is forcibly terminated

9-2. Improvements in Java 8u191+ and Java 11+

From certain Java 8 updates and in Java 11+, “UseContainerSupport” was introduced.

● Behavior in Containers

  • Can recognize cgroup‑based limits
  • Automatically calculates heap size within those limits

However, behavior still varies by version, so explicit configuration is recommended in production.

9-3. Explicitly Setting Heap Size in Containers (Required)

● Recommended Startup Pattern

docker run \
  --memory=1g \
  -e JAVA_OPTS="-Xms512m -Xmx800m" \
  my-java-app

Key points:

  • Container memory: 1GB
  • Java heap: keep it within 800MB
  • The rest is used by thread stacks and native memory

● Bad Example (Very Common)

docker run --memory=1g my-java-app   # no -Xmx

→ Java may allocate heap based on host memory, and once it crosses 1GB, you get OOMKilled.

9-4. Memory Settings Pitfalls in Kubernetes (K8s)

In Kubernetes, resources.limits.memory is critical.

● Pod Example

resources:
  limits:
    memory: "1024Mi"
  requests:
    memory: "512Mi"

In this case, keeping Java -Xmx at around 800MB to 900MB is typically safer.

● Why Set It Lower Than the Limit?

Because Java uses more than heap:

  • Native memory
  • Thread stacks (hundreds of KB × number of threads)
  • Metaspace
  • GC worker overhead
  • JIT‑compiled code
  • Library loading

Together, these can easily consume 100–300 MB.

In practice, a common rule is:

If limit = X, set -Xmx to about X × 0.7 to 0.8 for safety.

9-5. Automatic Heap Percentage in Java 11+ (MaxRAMPercentage)

In Java 11, heap size can be auto‑calculated using rules like:

● Default Settings

-XX:MaxRAMPercentage=25
-XX:MinRAMPercentage=50

Meaning:

  • Heap is capped at 25% of available memory
  • In small environments, it may use at least 50% as heap

● Recommended Setting

In containers, it’s often safer to set MaxRAMPercentage explicitly:

JAVA_OPTS="-XX:MaxRAMPercentage=70"

9-6. Why OOMKilled Happens So Often in Containers (Real‑World Pattern)

A common production pattern:

  1. K8s memory limit = 1GB
  2. No -Xmx configured
  3. Java references host memory and tries to allocate more than 1GB heap
  4. The container is forcibly terminated → OOMKilled

Note that this is not necessarily a java heap space (OutOfMemoryError) event—this is a container‑level OOM termination.

9-7. Container‑Specific Checkpoints Using GC Logs and Metrics

In container environments, focus especially on:

  • Whether pod restarts are increasing
  • Whether OOMKilled events are recorded
  • Whether the Old generation keeps growing
  • Whether GC reclaim drops sharply at certain times
  • Whether native (non‑heap) memory is running out

.Prometheus + Grafana hufanya hii iwe rahisi zaidi kuiona.

9-8. Muhtasari: “Mipangilio ya wazi” Ndiyo chaguo‑msingi katika Kontena

  • --memory peke yake huenda isimfanye Java kuhesabu heap kwa usahihi
  • Daima weka -Xmx
  • Acha nafasi ya ziada kwa kumbukumbu asili na viti vya nyuzi
  • Weka thamani chini ya mipaka ya kumbukumbu ya Kubernetes
  • Katika Java 11+, MaxRAMPercentage inaweza kuwa na manufaa

10. Vigezo Vya Kuzuia (Msimbo Mbovu / Mipangilio Mbovu)

Hitilafu ya “java heap space” hutokea si tu wakati heap haijitosha, bali pia wakati mifumo hatari ya uandishi wa msimbo au usanidi usio sahihi inatumiwa.

Hapa tunakusanya vigezo vya kawaida vya kuzuia vinavyokutana mara kwa mara katika kazi halisi.

10-1. Kuacha Mkusanyiko usio na Kikomo Kukua Milele

Moja ya matatizo yanayojitokeza mara kwa mara ni kuongezeka kwa makusanyo.

● Mfano Mbovu: Kuongeza kwenye Orodha Bila Kikomo

List<String> logs = new ArrayList<>();
while (true) {
    logs.add(getMessage());  // ← grows forever
}

Kwa muda mrefu wa uendeshaji, hili pekee linaweza kukusukuma kwa urahisi kwenye OOM.

● Kwa Nini Ni Hatari

  • GC haiwezi kurejesha kumbukumbu, na kizazi cha Kale kinapanuka
  • GC kamili huwa mara kwa mara, na kufanya programu iwe na uwezekano mkubwa wa kuganda
  • Kunakili idadi kubwa ya vitu huongeza mzigo wa CPU

● Jinsi ya Kuepuka Hii

  • Weka kikomo cha ukubwa (kwa mfano, kache ya LRU)
  • Safisha kwa kipindi
  • Usihifadhi data bila sababu

10-2. Kupakia Faili Kubwa au Data Zote Mara Moja

Hii ni kosa la kawaida katika usindikaji wa batch na upande wa seva.

● Mfano Mbovu: Kusoma JSON Kubwa Mara Moja

String json = Files.readString(Paths.get("large.json"));
Data d = mapper.readValue(json, Data.class);

● Nini Kinakwenda Sawa

  • Unahifadhi maandishi ya kabla ya kuchambua na vitu vya baada ya kuchambua kwenye kumbukumbu
  • Faili la 500 MB linaweza kutumia zaidi ya mara mbili ya ukubwa wake katika kumbukumbu
  • Vitu vya kati vinavyoongezwa vinatengenezwa, na heap inakamilika

● Jinsi ya Kuepuka Hii

  • Tumia mtiririko (usindikaji wa mlolongo)
  • Soma kwa vipande badala ya kupakia kwa wingi
  • Usihifadhi seti kamili ya data kwenye kumbukumbu

10-3. Kuendelea Kuhifadhi Data katika Vigezo vya static

● Mfano Mbovu

public class UserCache {
    private static Map<String, User> cache = new HashMap<>();
}

● Kwa Nini Ni Hatari

  • static inaishi muda wote JVM inapoendesha
  • Ikiwa inatumika kama kache, viingilio huenda visitolewe kamwe
  • Marejeleo yanabaki, na kuwa chanzo cha uvujaji wa kumbukumbu

● Jinsi ya Kuepuka Hii

  • Punguza matumizi ya static kadiri iwezekanavyo
  • Tumia mfumo maalum wa kache (kwa mfano, Caffeine)
  • Weka TTL na kikomo cha ukubwa wa juu

10-4. Kutumia Vibaya Stream / Lambda na Kutengeneza Orodha Kubwa za Kati

API ya Stream ni rahisi kutumia, lakini inaweza kutengeneza vitu vya kati ndani yake na kuweka shinikizo kwenye kumbukumbu.

● Mfano Mbovu (collect inaunda orodha kubwa ya kati)

List<Item> result = items.stream()
        .map(this::convert)
        .collect(Collectors.toList());

● Jinsi ya Kuepuka Hii

  • Chakata kwa mlolongo kwa kutumia for‑loop
  • Epuka kutengeneza orodha za kati zisizo za lazima
  • Ikiwa seti ya data ni kubwa, fikiria upya kutumia Stream katika sehemu hiyo

10-5. Kufanya Muungano Mkubwa wa Mstari kwa Opereta ya +

Kwa kuwa Strings haziwezi kubadilishwa, kila muungano huunda kipengele kipya cha String.

● Mfano Mbovu

String result = "";
for (String s : list) {
    result += s;
}

● Nini Niko Sawa

  • String mpya inaundwa kila mzunguko
  • Idadi kubwa ya matukio yanatengenezwa, ikisukuma kumbukumbu

● Jinsi ya Kuepuka Hii

StringBuilder sb = new StringBuilder();
for (String s : list) {
    sb.append(s);
}

10-6. Kuunda Kache Zingi Sana na Kutozisimamia

● Mifano Mbovu

  • Kuhifadhi majibu ya API katika Map bila kikomo
  • Kuendelea kuhifadhi picha au data za faili katika kache
  • Hakuna mfumo wa udhibiti kama LRU

● Kwa Nini Ni Hatari

  • Kache inaongezeka kwa muda
  • Kumbukumbu isiyoweza kurejeshwa inaongezeka
  • Itakuwa karibu kila wakati kuwa tatizo la uzalishaji

● Jinsi ya Kuepuka Hii

  • Tumia Caffeine / Guava Cache
  • Weka ukubwa wa juu
  • Sanidi TTL (mwisho wa muda)

10-7. Kuhifadhi Logi au Takwimu Kwenye Kumbukumbu Kwa Muda Mrefu

● Mfano Mbovu

List<String> debugLogs = new ArrayList<>();
debugLogs.add(message);

Katika uzalishaji, logi zinapaswa kuandikwa kwenye faili au mifumo ya logi. Kuzihifadhi katika kumbukumbu ni hatari.

10-8. Kutotaja -Xmx katika Kontena za Docker

Hii inachangia sehemu kubwa ya matukio ya heap ya kisasa.

● Mfano Mbovu

docker run --memory=1g my-app

● Nini Kiko Hapa

  • Java inaweza kubadilisha ukubwa wa heap kiotomatiki kulingana na kumbukumbu ya mwenyeji
  • Mara inapita kikomo cha kontena, hupata OOMKilled

● Jinsi ya Kuepuka Hii

docker run --memory=1g -e JAVA_OPTS="-Xmx700m"

10-9. Kubinafsisha Mipangilio ya GC Kupita Kiasi

Uboreshaji usio sahihi unaweza kurudisha matokeo hasi.

● Mfano Mbovu

-XX:MaxGCPauseMillis=10
-XX:G1HeapRegionSize=1m

Vigezo vya hali ya juu vinaweza kufanya GC kuwa mkali sana au kuzuia iendelee.

● Jinsi ya Kuepuka Hii

  • Katika hali nyingi, mipangilio ya chaguo-msingi inatosha
  • Boreshaji wa kidogo tu wakati kuna tatizo maalum, lililopimwa

10-10. Muhtasari: Mifumo Mikubwa ya Kosa Inatokana na “Kuhifadhi Mengi Sana”

Kile ambacho mifumo hii yote ya kosa ina pamoja ni:

“Kukusanya vitu zaidi ya vinavyohitajika.”

  • Mkusanyiko usio na kikomo
  • Uhifadhi usio wa lazima
  • Upakiaji wa wingi
  • Miundo yenye uzito wa statiki
  • Cache isiyodhibitiwa
  • Vitu vya kati vinavyoputika

Kuepuka haya pekee kunaweza kupunguza kwa kiasi kikubwa makosa ya “java heap space”.

11. Mifano Halisi: Msimbo Huu Ni Hatari (Mifumo ya Tatizo la Kumbukumbu ya Kawaida)

Sehemu hii inatambulisha mifano ya msimbo hatari inayokutana mara kwa mara katika miradi halisi ambayo mara nyingi husababisha makosa ya “java heap space”, na inaelezea kwa kila moja:
“Kwanini ni hatari” na “Jinsi ya kukirekebisha.”

Katika vitendo, mifumo hii mara nyingi hutokea pamoja, hivyo sura hii ni muhimu sana kwa mapitio ya msimbo na uchunguzi wa matukio.

11-1. Upakiaji wa Wingi wa Data Kubwa

● Mfano Mbovu: Kusoma Mistari Yote ya CSV Kubwa

List&lt;String&gt; lines = Files.readAllLines(Paths.get("big.csv"));

● Kwanini Ni Hatari

  • Kadiri faili inavyokuwa kubwa, ndivyo shinikizo la kumbukumbu linavyoongezeka
  • Hata CSV ya 100 MB inaweza kutumia kumbukumbu zaidi ya mara mbili kabla/baada ya kuchambua
  • Kuhifadhi rekodi kubwa sana kunaweza kumaliza kizazi cha Old generation

● Boresha: Soma Kupitia Mtiririko (Uchakataji wa Mfululizo)

try (Stream<String> stream = Files.lines(Paths.get("big.csv"))) {
    stream.forEach(line -> process(line));
}

→ Mstari mmoja tu unahifadhiwa katika kumbukumbu kwa wakati, na kufanya hii salama sana.

11-2. Mifumo ya Kuongezeka kwa Mkusanyiko

● Mfano Mbovu: Kukusanya Vitu Vizito Kwa Muda Mrefu Katika Orodha

List<Order> orders = new ArrayList<>();
while (hasNext()) {
    orders.add(fetchNextOrder());
}

● Kwanini Ni Hatari

  • Kila hatua ya ukuaji inarejesha upya safu ya ndani
  • Ikiwa huna haja ya kuhifadhi kila kitu, ni upotevu tu
  • Muda mrefu wa utekelezaji unaweza kutumia nafasi kubwa ya kizazi cha Old

● Boresha: Chakata Mfululizo + Batch Inapohitajika

while (hasNext()) {
    Order order = fetchNextOrder();
    process(order);      // process without retaining
}

Au batchi:

List<Order> batch = new ArrayList<>(1000);
while (hasNext()) {
    batch.add(fetchNextOrder());
    if (batch.size() == 1000) {
        processBatch(batch);
        batch.clear();
    }
}

11-3. Kuunda Vitu Vingi vya Kati Kupitia Stream API

● Mfano Mbovu: Orodha za kati zinazojirudia kupitia map → filter → collect

List<Data> result = list.stream()
        .map(this::convert)
        .filter(d -> d.isValid())
        .collect(Collectors.toList());

● Kwanini Ni Hatari

  • Huunda vitu vingi vya muda ndani
  • Hii ni hatari hasa na orodha kubwa
  • Kadiri bomba linavyokuwa ndefu, ndivyo hatari inavyoongezeka

● Boresha: Tumia for-loop au uchakataji wa mfululizo

List<Data> result = new ArrayList<>();
for (Item item : list) {
    Data d = convert(item);
    if (d.isValid()) {
        result.add(d);
    }
}

11-4. Kuchambua JSON au XML Mara Moja

● Mfano Mbovu

String json = Files.readString(Paths.get("large.json"));
Data data = mapper.readValue(json, Data.class);

● Kwa Nini Ni Hatari

  • Zote mbili, safu ya JSON ghafi na vitu vilivyotengenezwa, hubaki katika kumbukumbu
  • Kwa faili za darasa la 100MB, hepa inaweza kujazwa mara moja
  • Masuala yanayofanana yanaweza kutokea hata unapojaribu kutumia Stream APIs, kulingana na matumizi

● Uboreshaji: Tumia API ya Mtiririko

JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
    while (!parser.isClosed()) {
        JsonToken token = parser.nextToken();
        // Process only when needed and do not retain data
    }
}

11-5. Kupakia Picha Zote / Data ya Binary katika Kumbukumbu

● Mfano Mbovu

byte[] image = Files.readAllBytes(Paths.get("large.png"));

● Kwa Nini Ni Hatari

  • Data ya binary inaweza kuwa kubwa na “nzito” kwa asili
  • Katika programu za usindikaji picha, hii ni chanzo kikuu cha OOM

● Maboresho

  • Tumia buffering
  • Chakata kama mtiririko bila kuhifadhi faili nzima katika kumbukumbu
  • Kusoma kwa wingi logi zenye mamilioni ya mistari pia ni hatari

11-6. Uhifadhi Usio na Mwisho kupitia Cache ya Statiki

● Mfano Mbovu

private static final List<Session> sessions = new ArrayList<>();

● Nini Kiko Hapa

  • sessions haitatolewa hadi JVM iondoke
  • Inakua na muunganisho na hatimaye husababisha OOM

● Maboresho

  • Tumia cache inayodhibiti ukubwa (Caffeine, Guava Cache, nk.)
  • Dhibiti mzunguko wa maisha ya kikao kwa uwazi

11-7. Matumizi Mabovu ya ThreadLocal

● Mfano Mbovu

private static final ThreadLocal<SimpleDateFormat> formatter =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

ThreadLocal ni muhimu, lakini pamoja na pool za nyuzi inaweza kuweka thamani hai na kusababisha uvujaji.

● Maboresho

  • Weka ThreadLocal kuwa ya muda mfupi
  • Epuka kuitumia isipokuwa ni muhimu sana
  • Piga remove() kuiondoa

11-8. Kuunda Vighairi Vingi Sana

Hii mara nyingi inapuuzwa, lakini Vighairi ni vitu vizito sana kutokana na uzalishaji wa rekodi ya stack.

● Mfano Mbovu

for (...) {
    try {
        doSomething();
    } catch (Exception e) {
        // log only
    }
}

→ Kujaa vighairi kunaweza kushinikiza kumbukumbu.

● Maboresho

  • Usitumie vighairi kwa mtiririko wa kawaida wa udhibiti
  • Kataa ingizo batili kupitia uthibitishaji
  • Epuka kutupa vighairi isipokuwa ni lazima

11-9. Muhtasari: Msimbo Hatari “Kimya” Unakula Heapa Yako

Mada ya kawaida ni:
“miundo inayobana taratibu heapa, ikiwashwa juu ya kila moja.”

  • Upakiaji wa wingi
  • Mkusanyiko usio na mwisho
  • Kusahau kujitoa/kusafisha
  • Uundaji wa vitu vya kati
  • Kujaa vighairi
  • Uhifadhi wa statiki
  • Mabaki ya ThreadLocal

Katika hali zote, athari inajitokeza wakati wa utekelezaji mrefu.

12. Mazoea Mazuri ya Usimamizi wa Kumbukumbu ya Java (Muhimu Kuzuia Kurudiwa)

Hadi sasa, tumeshughulikia sababu za makosa ya “java heap space” na hatua za kukabiliana nazo kama upanuzi wa heapa, maboresho ya msimbo, urekebishaji wa GC, na uchunguzi wa uvujaji.

Sehemu hii inahitimisha mazoea bora yanayohakikisha kuzuia kurudiwa katika shughuli halisi.
Fikiria haya kama kanuni za chini kabisa ili kuweka programu za Java kuwa thabiti.

12-1. Weka Ukubwa wa Heapa Kwa Uwazi (Haswa katika Uzalishaji)

Kumilisha kazi za uzalishaji kwa usanidi chaguomsingi ni hatari.

● Mazoea Mazuri

  • Weka wazi -Xms na -Xmx
  • Usitumie usanidi chaguomsingi katika uzalishaji
  • Weka ukubwa wa heapa kuwa thabiti kati ya dev na prod (epuka tofauti zisizotarajiwa)

Mfano:

-Xms1g -Xmx1g

Katika Docker / Kubernetes, lazima uweke heapa ndogo ili iendane na mipaka ya kontena.

12-2. Simamia Vizuri (GC, Matumizi ya Kumbukumbu, OOM)

Matatizo ya heapa mara nyingi yanaweza kuzuishwa ikiwa utagundua ishara za awali.

● Nini cha Kufuatilia

  • Matumizi ya kizazi cha zamani
  • Mwenendo wa ukuaji wa kizazi kipya
  • Mzunguko wa Full GC
  • Wakati wa kusimama kwa GC
  • Matukio ya Container OOMKilled
  • Hesabu ya kuanza upya kwa Pod (K8s)

● Zana Zinazopendekezwa

  • VisualVM
  • JDK Mission Control
  • Prometheus + Grafana
  • Vipimo vya mtoa huduma wa wingu (k.m., CloudWatch)

Kuuza kwa polepole kwa matumizi ya kumbukumbu juu ya wakati mrefu wa utendaji ni ishara ya kawaida ya uvujaji wa kumbukumbu.

12-3. Tumia “Controlled Caches”

Cache runaway ni moja ya sababu za kawaida za OOM katika uzalishaji.

● Mazoea Bora

  • Tumia Caffeine / Guava Cache
  • Panga daima TTL (kukomesha)
  • Weka saizi kubwa zaidi (k.m., viingilio 1,000)
  • Epuka akiba ya static iwezekanavyo
    Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build();
    

12-4. Kuwa Makini na Kutumia Kupita Kiasi Stream API na Lambdas

Kwa data kubwa, kuunganisha shughuli za Stream huongeza vitu vya kati.

● Mazoea Bora

  • Usiungane map/filter/collect zaidi ya lazima
  • Chukua data kubwa kwa mpangilio na for-loop
  • Wakati wa kutumia collect, kuwa na ufahamu wa kiasi cha data

Streams ni rahisi, lakini si mara zote hurafiki na kumbukumbu.

12-5. Badilisha Faili Kubwa / Data Kubwa kwa Streaming

Kuchakata kwa wingi ni sababu kuu ya matatizo ya heap.

● Mazoea Bora

  • CSV → Files.lines()
  • JSON → Jackson Streaming
  • DB → paging
  • API → chunked fetch (cursor/pagination)

Ikiwa utatekeleza “usipakie kila kitu kwenye kumbukumbu,” matatizo mengi ya nafasi ya heap yatapotea.

12-6. Shughulikia ThreadLocal kwa Makini

ThreadLocal ni yenye nguvu, lakini matumizi mabaya yanaweza kusababisha uvujaji mkubwa wa kumbukumbu.

● Mazoea Bora

  • Kuwa makini hasa wakati wa kuunganishwa na thread pools
  • Piga simu remove() baada ya matumizi
  • Usihifadhi data ndefu ya maisha
  • Epuka static ThreadLocal iwezekanavyo

12-7. Piga Picha za Heap Mara kwa Mara ili Kugundua Uvujaji Mapema

Kwa mifumo inayoendesha muda mrefu ( programu za wavuti, mifumo ya batch, IoT), kupiga picha za heap mara kwa mara na kuzilinganisha husaidia kugundua ishara za uvujaji mapema.

● Chaguzi

  • VisualVM
  • jmap
  • -XX:+HeapDumpOnOutOfMemoryError
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/var/log/heapdump.hprof
    

Kutoa kiotomatiki kwenye OOM ni mpangilio wa lazima wa uzalishaji.

12-8. Weka Mbadala ya GC Kuwa Ndogo

Wazo “kubadala GC kutaongeza utendaji kiotomatiki” linaweza kuwa hatari.

● Mazoea Bora

  • Anza na mpangilio wa default
  • Fanya mabadiliko madogo tu wakati tatizo lililoapimwa lipo
  • Tumia G1GC kama chaguo la default
  • Katika hali nyingi, kuongeza heap ni bora zaidi kuliko kubadala kidogo

12-9. Fikiria Kugawanya Usanifu

Ikiwa kiasi cha data kinakuwa kikubwa sana au programu inakuwa monolithic sana na inahitaji heap kubwa, unaweza kuhitaji uboreshaji wa usanifu:

  • Microservices
  • Kugawanya uchakataji wa data ya batch
  • Kutenganisha na folofolo za ujumbe (Kafka, n.k.)
  • Uchakatishaji uliotawanyika (Spark, n.k.)

Ikiwa “hata kiasi gani cha heap utaongeza, haitoshi,” shuku tatizo la usanifu.

12-10. Muhtasari: Usimamizi wa Kumbukumbu ya Java Ni Kuhusu Ubora wa Tabaka

Matatizo ya nafasi ya heap hayashughulikiwi na mpangilio mmoja au marekebisho ya code moja.

● Vidokezo Muhimu

  • Weka daima heap wazi
  • Ufuatiliaji ni muhimu zaidi
  • Kamwe uruhusu bloat ya mkusanyiko
  • Tumia streaming kwa data kubwa
  • Simamia akiba vizuri
  • Tumia ThreadLocal kwa makini
  • Changanua uvujaji kwa zana wakati inahitajika
  • Vyombo vinahitaji mawazo tofauti

Kufuata pointi hizi kutazuia makosa mengi ya “java heap space” kwa uhakika mkubwa.

13. Muhtasari: Pointi Muhimu za Kuzuia Makosa ya “java heap space”

Katika makala hii, tulishughulikia makosa ya “java heap space” kutoka sababu za msingi hadi kupunguza na kuzuia kutokea tena.

Hapa tunapanga muhimu kama muhtasari wa vitendo.

13-1. Tatizo Halisi Sio “Heap Ni Ndogo Sana” Lakini “Kwa Nini Inakwisha?”

“java heap space” si upungufu rahisi wa kumbukumbu tu.

● Sababu ya msingi kawaida ni moja ya yafuatayo

. Ukubwa wa heap ni mdogo sana (usanidi usiotosha) * Usindikaji wa wingi wa data kubwa (suala la muundo) * Ukubwa wa mkusanyiko (ukosefu wa ufutaji/ubunifu) * Uvujaji wa kumbukumbu (marejeo yanabaki) * Usanidi usio sahihi katika kontena* (Docker/K8s-maalum)

Anza na: “Kwa nini heap ilikamilika?”

13-2. Hatua za Kwanza za Kuchunguza

① Thibitisha ukubwa wa heap

→ Weka wazi -Xms / -Xmx

② Elewa vikwazo vya kumbukumbu wakati wa utekelezaji

→ Katika Docker/Kubernetes, linganisha vizingiti na ukubwa wa heap
→ Pia angalia -XX:MaxRAMPercentage

③ Rekodi na chunguza log za GC

→ Ukuaji wa old-gen na Full GC za mara kwa mara ni ishara za onyo

④ Rekodi na uchambue dump za heap

→ Tumia VisualVM / MAT kuanzisha ushahidi wa uvujaji

13-3. Mifumo ya Hatari ya Juu Inayojulikana katika Uzalishaji

Kama ilivyoonyeshwa katika makala hii, mifumo ifuatayo mara nyingi husababisha matukio:

  • Usindikaji wa wingi wa faili kubwa
  • Kuongeza kwenye List/Map bila kikomo
  • Cache isiyodhibitiwa
  • Kukusanya data katika static
  • Viumbile vya kati vinavyopiga mbio kupitia minyororo ya Stream
  • Kutumia ThreadLocal vibaya
  • Kutoweka -Xmx katika Docker

Kama unaona haya katika msimbo au mipangilio, chunguza kwanza.

13-4. Marekebisho ya Msingi Yanahusu Ubunifu wa Mfumo na Usindikaji wa Data

● Nini cha kukagua katika ngazi ya mfumo

  • Badilisha usindikaji wa data kubwa kwa usindikaji wa mtiririko
  • Tumia caches zenye TTL, vizingiti vya ukubwa, na utowaji
  • Fanya ufuatiliaji wa kumbukumbu wa mara kwa mara kwa programu zinazoendesha muda mrefu
  • Chunguza ishara za uvujaji mapema kwa zana

● Ikiwa bado ni ngumu

  • Tenganisha usindikaji wa batch na mtandaoni
  • Microservices
  • Kumbatia majukwaa ya usindikaji uliogawanyika (Spark, Flink, nk.)

Maboresho ya usanifu yanaweza kutakiwa.

13-5. Ujumbe Tatu Muhimu Zaidi

Kama utakumbuka vitu vitatu tu:

✔ Weka heap wazi kila wakati

✔ Usisindike data kubwa kwa wingi

✔ Huwezi kuthibitisha uvujaji bila dump za heap

Vitu hivi vitatu pekee vinaweza kupunguza sana matukio muhimu ya uzalishaji yanayosababishwa na “java heap space.”

13-6. Usimamizi wa Kumbukumbu ya Java ni Ujuzi Unaotoa Faida Halisi

Usimamizi wa kumbukumbu ya Java unaweza kuonekana mgumu, lakini ukijua:

  • Uchunguzi wa matukio unakuwa haraka sana
  • Mifumo yenye mzigo mkubwa inaweza kuendeshwa kwa uthabiti
  • Urekebishaji wa utendaji unakuwa sahihi zaidi
  • Unakuwa mhandisi anayeelewa programu na miundombinu

Si kupuuza kusema ubora wa mfumo unalingana na uelewa wa kumbukumbu.

14. Maswali Yanayoulizwa Mara kwa Mara (FAQ)

Hatimaye, hapa kuna sehemu ya maswali na majibu ya vitendo inayoshughulikia maswali ya kawaida watu wanayotafuta kuhusu “java heap space.”

Hii inakamilisha makala na kusaidia kunasa wigo mpana wa nia ya mtumiaji.

Q1. To tofauti kati ya java.lang.OutOfMemoryError: Java heap space na GC overhead limit exceeded?

● java heap space

  • Hutokea wakati heap imekushindwa kabisa
  • Mara nyingi husababishwa na data kubwa, mkusanyiko unaokua kupita kiasi, au usanidi usiotosha

● GC overhead limit exceeded

  • GC inafanya kazi kwa bidii lakini hairejeshi chochote
  • Ishara kwamba GC haiwezi kupona kutokana na vitu vingi vya hai
  • Mara nyingi inaashiria uvujaji wa kumbukumbu au marejeo yanayodumu

Mfano wa kiakili unaofaa:
heap space = tayari imevuka kizingiti, GC overhead = kabla tu ya kizingiti.

Q2. Ikiwa nitongeza heap tu, je, itatatuliwa?

✔ Inaweza kusaidia kwa muda mfupi

✘ Haiwezi kutatua chanzo kikuu

  • Ikiwa heap ni ndogo sana kwa mzigo wako wa kazi → itasaidia
  • Ikiwa mkusanyiko au uvujaji ndio chanzo → itarudi tena

Kama chanzo ni uvujaji, kuongeza mara mbili heap itachelewa tu OOM ijayo.

Q3. Naweza kuongeza heap ya Java kiasi gani?

● Kawaida: 50%–70% ya kumbukumbu ya kimwili

Kwa sababu lazima uhifadhi kumbukumbu kwa:

  • Kumbukumbu asili
  • Stacks za Thread
  • Metaspace
  • Wafanyakazi wa GC
  • Mchakato wa OS

Haswa katika Docker/K8s, ni kawaida kuweka:
-Xmx = 70%–80% ya kizingiti cha kontena.

Q4. Kwa nini Java hupata OOMKilled katika kontena (Docker/K8s)?

● Katika hali nyingi, kwa sababu -Xmx haijawekwa

Docker inaweza isipeleze kikamilifu mipaka ya kontena kwa Java, hivyo Java inakadiria ukubwa wa heap kulingana na kumbukumbu ya mwenyeji → inavuka kipimo → OOMKilled.

✔ Suluhisho

docker run --memory=1g -e JAVA_OPTS="-Xmx800m"

Q5. Je, kuna njia rahisi ya kutambua ikiwa ni uvujaji wa kumbukumbu?

✔ Kama haya ni kweli, inawezekana sana ni uvujaji

  • Matumizi ya heap yanaendelea kuongezeka kadiri muda unavyopita
  • Kumbukumbu haipungui sana hata baada ya GC kamili
  • Old‑gen inaongezeka kwa mtindo wa “ngazi‑nyumba”
  • OOM hutokea baada ya masaa au siku
  • Uendeshaji mfupi unaonekana sawa

Hata hivyo, uthibitisho wa mwisho unahitaji uchambuzi wa dump ya heap (Eclipse MAT).

Q6. Mipangilio ya heap katika Eclipse / IntelliJ haijatekelezwa

● Sababu za kawaida

  • Hujabadilisha Run Configuration
  • Mipangilio ya chaguo‑msingi ya IDE inachukua kipaumbele
  • JAVA_OPTS ya script nyingine ya kuanzisha inabadili mipangilio yako
  • Umesahau kuanzisha upya mchakato

Mipangilio ya IDE inatofautiana, kwa hivyo daima angalia sehemu ya “VM options” katika Run/Debug Configuration.

Q7. Je, ni kweli kwamba Spring Boot hutumia kumbukumbu nyingi?

Ndiyo. Spring Boot mara nyingi hutumia kumbukumbu zaidi kutokana na:

  • Usanidi otomatiki
  • Beans nyingi
  • Upakiaji wa darasa katika JAR nzito
  • Seva ya wavuti iliyojumuishwa (Tomcat, nk.)

Ikilinganishwa na programu ya Java rahisi, inaweza kutumia takriban ~200–300 MB zaidi katika baadhi ya hali.

Q8. Ninapaswa kutumia GC gani?

Katika hali nyingi, G1GC ni chaguo salama.

● Mapendekezo kulingana na mzigo wa kazi

  • Programu za wavuti → G1GC
  • Kazi za batch zenye upitishaji mkubwa → Parallel GC
  • Mahitaji ya latency ya chini sana → ZGC / Shenandoah

Isipokuwa na sababu imara, chagua G1GC.

Q9. Nipaswa kushughulikiaje heap katika mazingira ya serverless (Cloud Run / Lambda)?

Mazingira ya serverless yana mipaka mikali ya kumbukumbu, hivyo unapaswa kusanidi heap waziwazi.

Mfano (Java 11):

-XX:MaxRAMPercentage=70

Pia kumbuka kuwa kumbukumbu inaweza kupanda ghafla wakati wa kuanza baridi, hivyo acha nafasi ya ziada katika usanidi wako wa heap.

Q10. Ninawezaje kuzuia matatizo ya heap ya Java kurudi tena?

Ukifuata kwa ukamilifu sheria hizi tatu, kurudi tena kunapungua sana:

✔ Sanidi heap waziwazi

✔ Chakata data kubwa kupitia mtiririko

✔ Kagua mara kwa mara log za GC na dump za heap

Summary: Tumia FAQ kuondoa mashaka na kutekeleza hatua za kukabiliana na kumbukumbu za kiutendaji

FAQ hii ilijumuisha maswali ya kawaida yanayotokana na utafutaji kuhusu “java heap space” na majibu ya vitendo.

Pamoja na makala kuu, inapaswa kukusaidia kuwa na ujuzi wa kushughulikia matatizo ya kumbukumbu ya Java na kuboresha kwa kiasi kikubwa uimara wa mfumo katika uzalishaji.