Rumah > Java > JavaAsas > Apakah kebocoran memori dan limpahan memori dalam java

Apakah kebocoran memori dan limpahan memori dalam java

青灯夜游
Lepaskan: 2023-01-13 00:40:03
asal
15599 orang telah melayarinya

Kebocoran memori bermakna selepas program menggunakan memori, ia tidak dapat melepaskan ruang memori yang diperuntukkan. Limpahan memori bermakna apabila program menggunakan memori, tidak ada memori yang mencukupi untuk digunakan oleh pemohon atau ia menyediakan ruang storan untuk menyimpan data int, tetapi data yang lama disimpan, akibatnya memori tidak mencukupi, dan ralat OOM dilaporkan. Pengumpulan kebocoran memori akhirnya akan menyebabkan limpahan ingatan.

Apakah kebocoran memori dan limpahan memori dalam java

Persekitaran pengendalian tutorial ini: sistem Windows 7, versi Java 8, komputer DELL G3.

1. Kebocoran memori:

bermakna selepas program menggunakan memori, ia tidak dapat melepaskan ruang memori yang digunakan berlaku Ia mempunyai impak yang besar, tetapi akibat pengumpulan kebocoran memori adalah limpahan memori.

2. Memori melimpah keluar dari ingatan:

bermakna apabila program memohon memori, tidak ada memori yang mencukupi untuk digunakan oleh pemohon, atau dengan kata lain , ia memberi anda ruang storan data jenis int storan, tetapi jika anda menyimpan data jenis panjang, maka keputusannya ialah memori tidak mencukupi, dan ralat OOM akan dilaporkan, iaitu limpahan memori yang dipanggil.

3 Hubungan antara keduanya:

  • Pengumpulan kebocoran memori akhirnya akan menyebabkan limpahan ingatan

  • Limpahan memori bermakna ruang memori yang anda inginkan melebihi ruang yang sebenarnya diperuntukkan kepada anda oleh sistem Pada masa ini, sistem adalah bersamaan dengan tidak dapat memenuhi keperluan anda dan akan melaporkan ralat limpahan memori.

  • Kebocoran memori bermakna anda memohon kepada sistem untuk memperuntukkan memori untuk digunakan (baharu), tetapi tidak mengembalikannya (memadam) selepas menggunakannya. Akibatnya, anda juga kehilangan memori yang anda mohon ia tidak boleh diakses lagi (mungkin anda kehilangan alamatnya), dan sistem tidak boleh memperuntukkannya kepada program yang diperlukan lagi. Ia sama dengan menyewa kabinet dengan kunci Selepas anda menyimpan barang-barang anda dan mengunci kabinet, anda kehilangan kunci atau gagal memulangkan kunci tersebut mengitar semula kerana ia tidak dapat mencari maklumat tentangnya.

  • Limpahan ingatan: Pinggan hanya boleh memuatkan 4 buah melalui pelbagai kaedah Anda meletakkan 5, tetapi ia jatuh ke tanah dan tidak boleh dimakan. Ini adalah limpahan. Sebagai contoh, jika tindanan ditolak apabila tindanan penuh, limpahan ruang akan berlaku, yang dipanggil limpahan Jika tindanan ditolak apabila tindanan kosong, limpahan ruang akan berlaku, yang dipanggil aliran bawah. Iaitu, memori yang diperuntukkan tidak mencukupi untuk memegang jujukan item data, yang dipanggil limpahan memori. Secara terang-terangan, saya tidak dapat menanggungnya, jadi saya akan melaporkan ralat.

4. Klasifikasi kebocoran memori (dikelaskan mengikut cara ia berlaku)

  • Kebocoran memori biasa. Kod dengan kebocoran memori akan dilaksanakan beberapa kali, menyebabkan kebocoran memori setiap kali ia dilaksanakan.

  • Kebocoran ingatan sekali-sekala. Kod yang menyebabkan kebocoran memori hanya akan berlaku dalam keadaan atau operasi tertentu. Kerap dan sporadis adalah relatif. Untuk keadaan tertentu, perkara yang sekali-sekala mungkin menjadi perkara biasa. Jadi persekitaran ujian dan kaedah ujian adalah penting untuk mengesan kebocoran memori.

  • Kebocoran memori satu kali. Kod yang menyebabkan kebocoran memori hanya akan dilaksanakan sekali, atau disebabkan kelemahan algoritma, akan sentiasa ada hanya satu dan hanya satu blok memori yang bocor. Sebagai contoh, jika memori diperuntukkan dalam pembina kelas, tetapi memori tidak dikeluarkan dalam pemusnah, kebocoran memori hanya akan berlaku sekali.

  • Kebocoran ingatan tersirat. Program ini secara berterusan memperuntukkan memori semasa ia berjalan, tetapi tidak melepaskan memori sehingga tamat. Tegasnya, tiada kebocoran memori di sini, kerana program akhirnya mengeluarkan semua memori yang diminta. Tetapi untuk program pelayan yang perlu dijalankan selama beberapa hari, minggu atau bulan, kegagalan untuk melepaskan memori dalam masa juga boleh menyebabkan keletihan akhirnya semua memori sistem. Oleh itu, kami memanggil jenis kebocoran memori ini sebagai kebocoran ingatan tersirat.

5 Punca dan penyelesaian limpahan ingatan:

(1) Punca limpahan ingatan:

  • Jumlah data yang dimuatkan dalam memori terlalu besar, seperti mengambil terlalu banyak data daripada pangkalan data pada satu masa

  • Terdapat rujukan kepada objek dalam kelas koleksi. Ia tidak dikosongkan pada penghujungnya, supaya JVM tidak boleh mengitar semula

  • Terdapat gelung tak terhingga dalam kod atau gelung menjana terlalu banyak entiti objek pendua; ;

  • menggunakan BUG dalam perisian pihak ketiga; >

  • (2) Penyelesaian kepada limpahan memori Penyelesaian:
  • Langkah pertama ialah mengubah suai parameter permulaan JVM dan terus meningkatkan memori. (Pastikan anda menambah parameter -Xms dan -Xmx.)
Langkah kedua ialah menyemak log ralat untuk melihat sama ada terdapat pengecualian atau ralat lain sebelum ralat "OutOfMemory".

Langkah ketiga ialah menelusuri dan menganalisis kod untuk mengetahui tempat limpahan memori mungkin berlaku.

Fokus pada perkara berikut:

  • Semak sama ada terdapat pertanyaan untuk mendapatkan semua data sekali gus dalam pertanyaan pangkalan data. Secara umumnya, jika seratus ribu rekod diambil ke dalam ingatan pada satu masa, ia boleh menyebabkan limpahan memori. Masalah ini agak tersembunyi Sebelum pergi ke dalam talian, terdapat kurang data dalam pangkalan data dan kurang berkemungkinan menyebabkan masalah Selepas pergi ke dalam talian, terdapat lebih banyak data dalam pangkalan data, dan satu pertanyaan boleh menyebabkan limpahan memori. Oleh itu, cuba gunakan paging untuk pertanyaan pangkalan data.

  • Semak sama ada terdapat gelung tak terhingga atau panggilan rekursif dalam kod.

  • Semak sama ada terdapat gelung besar yang berulang kali menjana entiti objek baharu.

  • Semak sama ada terdapat pertanyaan untuk mendapatkan semua data sekali gus dalam pertanyaan pangkalan data. Secara umumnya, jika 100,000 rekod diambil ke dalam ingatan pada satu masa, ia boleh menyebabkan limpahan memori. Masalah ini agak tersembunyi Sebelum pergi ke dalam talian, terdapat kurang data dalam pangkalan data dan kurang berkemungkinan menyebabkan masalah Selepas pergi ke dalam talian, terdapat lebih banyak data dalam pangkalan data, dan satu pertanyaan boleh menyebabkan limpahan memori. Oleh itu, cuba gunakan paging untuk pertanyaan pangkalan data.

  • Semak sama ada objek koleksi seperti Senarai dan MAP tidak dikosongkan selepas digunakan. Objek koleksi seperti Senarai dan MAP akan sentiasa mempunyai rujukan kepada objek, menjadikan objek ini tidak dapat dikitar semula oleh GC.

Langkah keempat, gunakan alat melihat memori untuk melihat penggunaan memori secara dinamik

Model memori JVM8

Sepuluh senario limpahan memori

Apabila JVM berjalan, ia memerlukan pemuat kelas (classLoader) untuk memuatkan fail bytecode kelas yang diperlukan. Selepas memuatkan, ia diserahkan kepada enjin pelaksanaan untuk pelaksanaan Semasa proses pelaksanaan, tempoh ruang diperlukan untuk menyimpan data (sama dengan CPU dan memori utama). Proses peruntukan dan pelepasan ruang memori ini adalah kawasan data masa jalan yang perlu kita ambil berat. Limpahan memori berlaku apabila pemuat kelas sedang memuatkan Limpahan memori dibahagikan kepada dua kategori: OutOfMemoryError dan StackOverflowError. Berikut menyenaraikan 10 situasi limpahan memori dan menerangkan cara limpahan memori berlaku melalui kod contoh.

1.Java heap memory overflow

Apabila java.lang.OutOfMemoryError: Java heap space exception berlaku, ini bermakna heap memory overflow .

1), penerangan masalah

  • Set memori jvm terlalu kecil dan memori yang diperlukan untuk objek terlalu besar diperuntukkan semasa mencipta objek Pengecualian ini akan dibuang.

  • Trafik/data memuncak, terdapat had tertentu untuk pemprosesan aplikasi itu sendiri, seperti bilangan pengguna tertentu atau jumlah data tertentu. Apabila bilangan pengguna atau jumlah data tiba-tiba melonjak dan melebihi ambang yang dijangkakan, maka operasi yang berjalan seperti biasa sebelum hentian puncak akan berhenti dan mencetuskan java lang.OutOfMemoryError: Ralat ruang timbunan Java

2), contoh kod

susun kod berikut, dan tetapkan parameter jvm kepada -Xms20m -Xmx20m apabila melaksanakan Jika 5m memori diperuntukkan sekali, jumlah permintaan akan menjadi kecil dan kutipan sampah akan menjadi normal dan tiada ralat akan berlaku. Walau bagaimanapun, apabila konkurensi berlaku, nilai memori maksimum akan melebihi dan limpahan memori akan dibuang.

3. Penyelesaian

Pertama sekali, jika tiada masalah dengan kod, anda boleh melaraskan dua parameter jvm -Xms dan -Xmx dengan sewajarnya, dan gunakan ujian tekanan untuk melaraskan ini dua parameter untuk mencapai nilai optimum.

Kedua, cuba elakkan aplikasi untuk objek besar, seperti muat naik fail dan dapatkan kumpulan besar daripada pangkalan data Ini perlu dielakkan pelaksanaan sistem.

Akhir sekali, cuba tingkatkan kelajuan pelaksanaan permintaan Lebih awal kutipan sampah, lebih baik jika tidak, apabila sejumlah besar permintaan serentak datang, memori tidak boleh diperuntukkan untuk permintaan baharu. menyebabkan runtuhan salji sistem.

2. Kebocoran memori timbunan Java

1), penerangan masalahKebocoran memori di Java ialah beberapa objek yang bukan Ia digunakan semula oleh aplikasi tetapi tidak boleh diiktiraf oleh kutipan sampah. Oleh itu, objek yang tidak digunakan ini masih wujud dalam ruang timbunan Java selama-lamanya. Pengumpulan berterusan akhirnya akan mencetuskan java.lang.OutOfMemoryError.

2), contoh kod

Apabila melaksanakan kod di atas, anda mungkin menjangkakan ia akan berjalan selama-lamanya tanpa sebarang masalah, dengan mengandaikan bahawa penyelesaian caching tulen hanya memetakan Skala asas kepada 10,000 elemen dan bukannya semua kunci yang sudah ada dalam HashMap. Walau bagaimanapun, sebenarnya elemen akan terus ditambah kerana kelas utama tidak mengatasi kaedah equals()nya.

Lama kelamaan, kerana kod yang bocor sentiasa digunakan, hasil "cache" akhirnya memakan banyak ruang timbunan Java. Apabila memori bocor mengisi semua memori yang tersedia di kawasan timbunan, kutipan sampah tidak boleh membersihkannya, java.lang.OutOfMemoryError.

3), penyelesaian

Penyelesaian yang sepadan agak mudah: cuma tulis semula kaedah yang sama:

3 Limpahan memori tamat masa pengumpulan sampah

1), Penerangan Masalah Apabila aplikasi kehabisan semua memori yang tersedia, had overhed GC melebihi ralat dan GC gagal mengosongkannya beberapa kali, maka java.lang akan dicetuskan. Apabila JVM menghabiskan banyak masa untuk melaksanakan GC, dengan sedikit kesan, dan apabila keseluruhan proses GC melebihi had, ralat akan dicetuskan (masa GC konfigurasi jvm lalai melebihi 98% dan memori timbunan kitar semula adalah kurang daripada 2 %).

2), kod sampel

3), penyelesaian

Untuk mengurangkan kitaran hayat objek, cuba lakukan kutipan sampah secepat seboleh mungkin.

4. Limpahan memori Metaspace

1), penerangan masalah

Limpahan metaspace, sistem akan membuang java .lang .OutOfMemoryError: Metaspace. Sebab untuk masalah abnormal ini ialah sistem mempunyai banyak kod atau merujuk banyak pakej pihak ketiga, atau penjanaan kod dinamik dan pemuatan kelas digunakan, yang mengakibatkan jejak memori yang besar dalam metaspace.

2), Contoh kod

3), Penyelesaian

Secara lalai, saiz metaspace dihadkan hanya oleh memori setempat. Walau bagaimanapun, demi prestasi keseluruhan mesin, item ini harus ditetapkan sebanyak mungkin untuk mengelak daripada mematikan perkhidmatan keseluruhan mesin.

  • Optimumkan konfigurasi parameter untuk mengelak menjejaskan proses JVM lain

-XX:MetaspaceSize, saiz ruang awal, apabila nilai ini dicapai, kutipan sampah akan dicetuskan Pemunggahan jenis dilakukan, dan GC akan melaraskan nilai: jika sejumlah besar ruang dilepaskan, nilai itu diturunkan dengan sewajarnya jika sejumlah kecil ruang dilepaskan, nilai itu dinaikkan sewajarnya apabila ia tidak melebihi MaxMetaspaceSize.

-XX:MaxMetaspaceSize, ruang maksimum, tiada had secara lalai.

Selain daripada dua pilihan penentu saiz di atas, terdapat dua atribut berkaitan GC: -XX:MinMetaspaceFreeNisbah Selepas GC, peratusan kapasiti ruang Metaspace minimum dikurangkan kepada ruang yang diperuntukkan koleksi. -XX:MaxMetaspaceFreeNisbah, selepas GC, peratusan kapasiti ruang baki Metaspace maksimum dikurangkan kepada kutipan sampah yang disebabkan oleh ruang kosong.

  • Sebut pakej pihak ketiga dengan berhati-hati

Untuk pakej pihak ketiga, pastikan anda memilihnya dengan berhati-hati dan alih keluar pakej yang tidak diperlukan. Ini bukan sahaja akan membantu meningkatkan kelajuan penyusunan dan pembungkusan, tetapi juga membantu meningkatkan kelajuan penggunaan jauh.

  • Beri perhatian kepada rangka kerja yang menjana kelas dinamik

Untuk rangka kerja yang menggunakan sejumlah besar kelas yang dijana secara dinamik, ujian tekanan perlu dilakukan untuk sahkan kelas yang dijana secara dinamik Jika keperluan memori melebihi, pengecualian akan dibuang.

5 Limpahan memori memori langsung

1), penerangan masalah

Apabila menggunakan allocateDirect() dalam ByteBuffer Ia akan digunakan kadangkala. Banyak rangka kerja javaNIO (seperti netty) dirangkumkan sebagai kaedah lain Apabila masalah ini berlaku, java.lang.OutOfMemoryError: Pengecualian memori penimbal langsung akan dibuang.

Masalah yang sama akan berlaku jika anda secara langsung atau tidak langsung menggunakan kaedah allocateDirect dalam ByteBuffer tanpa mengosongkannya.

2), kod sampel

3), penyelesaian

Jika anda sering melakukan operasi yang serupa, anda boleh mempertimbangkan untuk menetapkan parameter: -XX: MaxDirectMemorySize , dan kosongkan memori dalam masa.

6. Limpahan memori tindanan

1), penerangan masalah

Apabila thread melaksanakan kaedah Java, JVM Bingkai tindanan baharu akan dibuat dan ditolak ke bahagian atas tindanan. Pada masa ini, bingkai tindanan baharu menjadi bingkai tindanan semasa Apabila kaedah dilaksanakan, bingkai tindanan digunakan untuk menyimpan parameter, pembolehubah tempatan, arahan perantaraan dan data lain.

Apabila kaedah memanggil dirinya secara rekursif, data yang dijana oleh kaedah baharu (yang juga boleh difahami sebagai bingkai tindanan baharu) akan ditolak ke bahagian atas tindanan Setiap kali kaedah memanggil dirinya sendiri, a salinan akan dibuat Data kaedah semasa ditolak ke tindanan. Oleh itu, setiap tahap rekursi memerlukan penciptaan bingkai tindanan baharu. Hasilnya ialah semakin banyak memori dalam tindanan akan digunakan dengan panggilan rekursif Jika rekursif memanggil diri mereka sendiri sejuta kali, sejuta bingkai tindanan akan dihasilkan. Ini akan menyebabkan limpahan memori tindanan.

2), contoh kod

3), penyelesaian

Jika memang terdapat panggilan rekursif dalam program dan limpahan tindanan berlaku, anda boleh meningkatkan nilai saiz -Xss boleh menyelesaikan masalah limpahan memori tindanan. Panggilan rekursif menghalang pembentukan gelung tak terhingga, jika tidak limpahan memori tindanan akan berlaku.

7 Limpahan memori apabila mencipta utas setempat

1), penerangan masalah

Benang pada asasnya hanya menduduki kawasan memori di luar timbunan, dan juga Ralat ini menunjukkan bahawa kecuali untuk kawasan selain timbunan, kawasan memori tidak boleh diperuntukkan untuk utas Ini sama ada kerana memori itu sendiri tidak mencukupi, atau ruang timbunan ditetapkan terlalu besar, mengakibatkan tidak banyak baki memori, dan disebabkan oleh benang Ia mengambil ingatan itu sendiri, jadi ia tidak mencukupi.

2), contoh kod

3), penyelesaian

Mula-mula semak sama ada sistem pengendalian mempunyai had pada bilangan utas , gunakan Shell Threads juga tidak boleh dibuat Jika ini masalahnya, anda perlu melaraskan bilangan maksimum fail yang boleh disokong oleh sistem.

Dalam pembangunan harian, cuba pastikan bilangan maksimum benang boleh dikawal, dan jangan gunakan kumpulan benang sewenang-wenangnya. Ia tidak boleh berkembang tanpa had.

8 Limpahan memori di luar kawasan swap

1), penerangan masalah

Semasa proses permulaan aplikasi Java , Memori yang diperlukan boleh dihadkan dengan menentukan -Xmx dan parameter permulaan lain yang serupa. Apabila jumlah memori yang diminta oleh JVM lebih besar daripada memori fizikal yang tersedia, sistem pengendalian mula menukar kandungan daripada memori kepada cakera keras.

Secara umumnya, JVM akan membuang ralat ruang Out of swap, yang bermaksud bahawa apabila aplikasi gagal meminta timbunan asli JVM untuk memperuntukkan memori dan timbunan asli hampir habis, mesej ralat mengandungi saiz kegagalan peruntukan (dalam bahagian perkataan) dan sebab kegagalan permintaan.

2) Penyelesaian

Meningkatkan saiz kawasan swap sistem Saya secara peribadi berpendapat bahawa jika kawasan swap digunakan, prestasi akan dikurangkan dengan banyaknya dielakkan dalam persekitaran pengeluaran Memori maksimum melebihi memori fizikal sistem. Kedua, keluarkan kawasan swap sistem dan hanya gunakan memori sistem untuk memastikan prestasi aplikasi.

9 Limpahan memori limpahan tatasusunan

1). had. Secara umumnya, Java mempunyai had pada saiz maksimum tatasusunan yang boleh diperuntukkan oleh aplikasi Walau bagaimanapun, had tersebut berbeza-beza pada platform yang berbeza, tetapi ia biasanya antara 1 dan 2.1 bilion elemen. Apabila saiz tatasusunan yang diminta melebihi ralat had VM berlaku, ini bermakna aplikasi cuba memperuntukkan tatasusunan yang lebih besar daripada yang boleh disokong oleh mesin maya Java. Sebelum JVM memperuntukkan memori untuk tatasusunan, ia melakukan semakan khusus platform: sama ada struktur data yang diperuntukkan boleh ditangani pada platform ini.

2), contoh kod

Berikut ialah kod, yang bermaksud tatasusunan melebihi had maksimum.

3), penyelesaian

Oleh itu, panjang tatasusunan mestilah dalam julat panjang yang dibenarkan oleh platform. Walau bagaimanapun, ralat ini biasanya jarang berlaku, terutamanya kerana indeks tatasusunan Java adalah jenis int. Integer positif terbesar di Jawa ialah 2^31 - 1 = 2,147,483,647. Dan had khusus platform boleh menjadi sangat hampir dengan nombor ini, contohnya: pada persekitaran saya (macOS 64-bit, menjalankan Jdk1.8) saya boleh memulakan tatasusunan dengan panjang sehingga 2,147,483,645 (Integer.MAX_VALUE-2). Jika panjang tatasusunan ditambah 1 untuk mencapai nteger.MAX_VALUE-1, OutOfMemoryError akan berlaku.

10. Sistem mematikan limpahan memori proses

1). sistem pengendalian : Sistem pengendalian dibina berdasarkan konsep proses ini berfungsi dalam kernel. Terdapat proses yang sangat istimewa yang dipanggil "Out of memory killer". Apabila kernel mengesan bahawa sistem mempunyai memori yang tidak mencukupi, pembunuh OOM diaktifkan, menyemak proses mana yang menduduki paling banyak memori pada masa ini, dan kemudian mematikan proses tersebut.

Secara amnya, Kehabisan memori: Proses membunuh atau mengorbankan ralat kanak-kanak akan dicetuskan apabila memori maya maya yang tersedia (termasuk ruang swap) digunakan sehingga keseluruhan sistem pengendalian berisiko. Dalam kes ini, OOM Killer memilih "proses penyangak" dan membunuhnya.

2), kod sampel

3), penyelesaian

Walaupun peningkatan ruang swap boleh mengurangkan pengecualian ruang timbunan Java, Ia masih disyorkan bahawa penyelesaian terbaik adalah untuk menaik taraf memori sistem supaya aplikasi Java mempunyai memori yang mencukupi, jadi masalah ini tidak akan berlaku.

Ringkasan: Melalui 10 jenis situasi limpahan memori di atas, semua orang akan tahu cara menyelesaikan masalah apabila mereka benar-benar menghadapinya Anda juga harus ingat dalam pengekodan sebenar:

  • Pakej balang pihak ketiga harus diperkenalkan dengan berhati-hati, dan pakej balang yang tidak berguna harus dialih keluar dengan tegas untuk meningkatkan kelajuan kompilasi dan penggunaan memori sistem.

  • Untuk objek yang besar atau sejumlah besar aplikasi memori, pengoptimuman mesti dilakukan dalam bentuk kepingan untuk meningkatkan prestasi pemprosesan dan mengurangkan kitaran hayat objek.

  • Cuba betulkan bilangan utas untuk memastikan bahawa memori yang diduduki oleh utas boleh dikawal Apabila sejumlah besar utas diperlukan, bilangan maksimum sambungan terbuka sistem pengendalian mesti dioptimumkan.

  • Untuk panggilan rekursif, tahap rekursif juga mesti dikawal, tidak terlalu tinggi, melebihi kedalaman tindanan.

  • Semakin besar memori yang diperuntukkan kepada timbunan tidak, semakin baik, kerana semakin besar ingatan timbunan dan lebih banyak benang, tidak banyak ruang yang tinggal untuk timbunan, dan ia adalah mudah untuk membuang OOM. Secara amnya tiada masalah dengan parameter lalai JVM (termasuk rekursi).

Tutorial video berkaitan yang disyorkan: Tutorial video Java

Atas ialah kandungan terperinci Apakah kebocoran memori dan limpahan memori dalam java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan