Rumah  >  Artikel  >  hujung hadapan web  >  Pemahaman mendalam tentang pengurusan memori JavaScript dan algoritma GC

Pemahaman mendalam tentang pengurusan memori JavaScript dan algoritma GC

WBOY
WBOYke hadapan
2022-07-26 13:50:321991semak imbas

Artikel ini membawakan anda pengetahuan yang berkaitan tentang javascript terutamanya memperkenalkan pemahaman mendalam tentang pengurusan memori JavaScript dan algoritma GC Ia terutamanya menerangkan mekanisme pengumpulan sampah JavaScript dan juga algoritma pengumpulan sampah Artikel ini menerangkan pengurusan memori dalam enjin V8 Saya harap ia akan membantu semua orang.

Pemahaman mendalam tentang pengurusan memori JavaScript dan algoritma GC

[Cadangan berkaitan: tutorial video javascript, bahagian hadapan web]

Kata Pengantar

Apabila JavaScript mencipta pembolehubah (tatasusunan, rentetan, objek, dsb.), ia memperuntukkan memori secara automatik dan akan "secara automatik" mengeluarkan kandungan yang diperuntukkan apabila bahasa JavaScript tidak digunakan; bahasa asas lain Bahasa adalah sama, seperti bahasa C Mereka menyediakan antara muka pengurusan memori, seperti malloc() untuk memperuntukkan ruang memori yang diperlukan dan free() untuk melepaskan ruang memori yang diperuntukkan sebelum ini.

Kami memanggil proses melepaskan pengumpulan sampah memori Bahasa peringkat tinggi ​​seperti JavaScript menyediakan memori peruntukan automatik dan automatik kitar semula. Kerana ini secara automatik menyebabkan ramai pembangun tidak mengambil berat tentang pengurusan memori.

Walaupun bahasa peringkat tinggi menyediakan pengurusan memori automatik, kita masih perlu mempunyai pemahaman asas tentang pengurusan dalaman Kadangkala pengurusan memori automatik mempunyai masalah, kita boleh menyelesaikannya dengan lebih baik, atau menggunakan Cara paling murah untuk menyelesaikannya.

Kitaran hayat ingatan

Malah, tidak kira bahasa apa pun, kitaran pengisytiharan ingatan dibahagikan secara kasar kepada peringkat berikut:

Di bawah ini kami menerangkan setiap langkah secara terperinci:

  • Peruntukan memori: Apabila kami mentakrifkan pembolehubah, sistem akan secara automatik memperuntukkannya memori, yang membolehkan program menggunakan memori ini.
  • Penggunaan memori: Berlaku apabila membaca dan menulis pembolehubah
  • Kitar semula memori: Secara automatik selepas digunakan Membebaskan memori yang tidak diperlukan, iaitu, mekanisme kutipan sampah secara automatik menuntut semula memori yang tidak lagi digunakan

Peruntukan memori dalam JavaScript

Untuk melindungi rambut pembangun, peruntukan Memori JavaScript adalah dilengkapkan secara automatik apabila mentakrifkan pembolehubah. Kod sampel adalah seperti berikut:

let num = 123 // 给数值变量分配内存
let str = '一碗周' // 给字符串分配内存

let obj = {
  name: '一碗周',
  age: 18,
} // 给对象及其包含的值分配内存

// 给数组及其包含的值分配内存(类似于对象)
let arr = [1, null, 'abc']

function fun(a) {
  return a + 2
} // 给函数(可调用的对象)分配内存

// 函数表达式也能分配一个对象
Element.addEventListener(
  'click',
  event => {
    console.log(event)
  },
  false,
)

Kadangkala memori tidak akan diperuntukkan semula, seperti kod berikut:

// 给数组及其包含的值分配内存(类似于对象)
let arr = [1, null, 'abc']

let arr2 = [arr[0], arr[2]]
// 这里并不会重新对分配内存,而是直接存储原来的那份内存

Menggunakan memori dalam JavaScript

Proses menggunakan nilai dalam JavaScript sebenarnya adalah operasi membaca dan menulis memori yang diperuntukkan. Pembacaan dan penulisan di sini mungkin menulis pembolehubah, membaca nilai pembolehubah, menulis nilai atribut objek, dan menghantar parameter kepada fungsi, dsb.

Memori keluaran

Keluaran memori dalam JavaScript adalah automatik Masa keluaran adalah apabila nilai tertentu (alamat memori) tidak lagi digunakan dan JavaScript akan mengeluarkan memori yang didudukinya secara automatik. .

Malah, kebanyakan masalah pengurusan ingatan berlaku pada peringkat ini. Tugas paling sukar di sini ialah mencari pembolehubah yang tidak diperlukan.

Walaupun bahasa peringkat tinggi kini mempunyai mekanisme pengumpulan sampah mereka sendiri, walaupun terdapat banyak algoritma kutipan sampah sekarang, mereka tidak boleh mengitar semula semua situasi yang melampau Ini sebabnya kita perlu mempelajari pengurusan memori dan pengumpulan sampah algoritma sebab.

Seterusnya, mari kita bincangkan pengumpulan sampah dalam JavaScript dan algoritma kutipan sampah yang biasa digunakan.

Pengumpulan sampah dalam JavaScript

Seperti yang kami katakan sebelum ini, pengurusan memori dalam JavaScript adalah automatik Memori diperuntukkan secara automatik apabila objek dibuat apabila objek tidak lagi dirujuk oleh atau tidak boleh diakses daripada akar, ia akan dikitar semula sebagai sampah.

Objek boleh dicapai dalam JavaScript hanyalah objek yang boleh diakses oleh

, sama ada melalui rujukan atau rantai skop, selagi ia boleh diakses, ia dipanggil boleh dicapai objek. Terdapat piawai untuk kebolehcapaian objek boleh dicapai, iaitu, sama ada ia boleh didapati bermula dari akar di sini boleh difahami sebagai objek pembolehubah global dalam JavaScript, iaitu dalam persekitaran penyemak imbas dan window dalam persekitaran Nod global.

Untuk lebih memahami konsep rujukan, lihat kod berikut:

let person = {
  name: '一碗周',
}
let man = person
person = null

Rajah adalah seperti berikut:

Mengikut gambar di atas, kita dapat lihat akhirnya { name: '一碗周' } ini tidak akan dikumpul sebagai sampah kerana masih ada rujukan.

Sekarang mari kita fahami objek yang boleh dicapai Kodnya adalah seperti berikut:

function groupObj(obj1, obj2) {
  obj1.next = obj2
  obj2.prev = obj1

  return {
    obj1,
    obj2,
  }
}
let obj = groupObj({ name: '大明' }, { name: '小明' })

调用groupObj()函数的的结果obj是一个包含两个对象的一个对象,其中obj.obj1next属性指向obj.obj2;而obj.obj2prev属性又指向obj.obj2。最终形成了一个无限套娃。

如下图:

现在来看下面这段代码:

delete obj.obj1
delete obj.obj2.prev

我们删除obj对象中的obj1对象的引用和obj.obj2中的prev属性对obj1的引用。

图解如下:

此时的obj1就被当做垃圾给回收了。

GC算法

GC是Garbage collection的简写,也就是垃圾回收。当GC进行工作的时候,它可以找到内存中的垃圾、并释放和回收空间,回收之后方便我们后续的进行使用。

在GC中的垃圾包括程序中不在需要使用的对象以及程序中不能再访问到的对象都会被当做垃圾。

GC算法就是工作时查找和回收所遵循的规则,常见的GC算法有如下几种:

  • 引用计数:通过一个数字来记录引用次数,通过判断当前数字是不是0来判断对象是不是一个垃圾。
  • 标记清除:在工作时为对象添加一个标记来判断是不是垃圾。
  • 标记整理:与标记清除类似。
  • 分代回收:V8中使用的垃圾回收机制。

引用计数算法

引用计数算法的核心思想就是设置一个引用计数器,判断当前引用数是否为0 ,从而决定当前对象是不是一个垃圾,从而垃圾回收机制开始工作,释放这块内存。

引用计数算法的核心就是引用计数器 ,由于引用计数器的存在,也就导致该算法与其他GC算法有所差别。

引用计数器的改变是在引用关系发生改变时就会发生变化,当引用计数器变为0的时候,该对象就会被当做垃圾回收。

现在我们通过一段代码来看一下:

// { name: '一碗周' } 的引用计数器 + 1
let person = {
  name: '一碗周',
}
// 又增加了一个引用,引用计数器 + 1
let man = person
// 取消一个引用,引用计数器 - 1
person = null
// 取消一个引用,引用计数器 - 1。此时 { name: '一碗周' } 的内存就会被当做垃圾回收
man = null

引用计数算法的优点如下:

  • 发现垃圾时立即回收;
  • 最大限度减少程序暂停,这里因为发现垃圾就立刻回收了,减少了程序因内存爆满而被迫停止的现象。

缺点如下:

  • 无法回收循环引用的对象;

就比如下面这段代码:

function fun() {
  const obj1 = {}
  const obj2 = {}
  obj1.next = obj2
  obj2.prev = obj1
  return '一碗周'
}
fun()

上面的代码中,当函数执行完成之后函数体的内容已经是没有作用的了,但是由于obj1obj2都存在不止1个引用,导致两种都无法被回收,就造成了空间内存的浪费。

  • 时间开销大,这是因为引用计数算法需要时刻的去监控引用计数器的变化。

标记清除算法

标记清除算法解决了引用计数算法的⼀些问题, 并且实现较为简单, 在V8引擎中会有被⼤量的使⽤到。

在使⽤标记清除算法时,未引用对象并不会被立即回收.取⽽代之的做法是,垃圾对象将⼀直累计到内存耗尽为⽌.当内存耗尽时,程序将会被挂起,垃圾回收开始执行.当所有的未引用对象被清理完毕 时,程序才会继续执行.该算法的核心思想就是将整个垃圾回收操作分为标记和清除两个阶段完成。

第一个阶段就是遍历所有对象,标记所有的可达对象;第二个阶段就是遍历所有对象清除没有标记的对象,同时会抹掉所有已经标记的对象,便于下次的工作。

为了区分可用对象与垃圾对象,我们需要在每⼀个对象中记录对象的状态。 因此, 我们在每⼀个对象中加⼊了⼀个特殊的布尔类型的域, 叫做marked。 默认情况下, 对象被创建时处于未标记状态。 所以, marked 域被初始化为false

进行垃圾回收完毕之后,将回收的内存放在空闲链表中方便我们后续使用。

标记清除算法最大的优点就是解决了引用计数算法无法回收循环引用的对象的问题 。就比如下面这段代码:

function fun() {
  const obj1 = {},
    obj2 = {},
    obj3 = {},
    obj4 = {},
    obj5 = {},
    obj6 = {}
  obj1.next = obj2

  obj2.next = obj3
  obj2.prev = obj6

  obj4.next = obj6
  obj4.prev = obj1

  obj5.next = obj4
  obj5.prev = obj6
  return obj1
}
const obj = fun()

当函数执行完毕后obj4的引用并不是0,但是使用引用计数算法并不能将其作为垃圾回收掉,而使用标记清除算法就解决了这个问题。

Algoritma tanda dan jelas juga mempunyai kekurangan Algoritma ini akan membawa kepada pemecahan memori dan alamat terputus Selain itu, walaupun tanda dan algoritma jelas digunakan untuk mencari objek sampah, ia tidak boleh dikosongkan dengan serta-merta. dilakukan untuk kali kedua.

Algoritma penandaan dan pengisihan

Algoritma penandaan dan pengisihan boleh dianggap sebagai versi dipertingkatkan bagi algoritma tanda dan jelas, dan langkahnya juga dibahagikan kepada dua peringkat: menanda dan mengosongkan.

Walau bagaimanapun, fasa pembersihan algoritma pengisihan tanda akan terlebih dahulu mengisih, menggerakkan kedudukan objek, dan akhirnya mengosongkan.

Langkah-langkahnya adalah seperti berikut:

Pengurusan memori dalam V8

Apakah itu V8

V8 ialah enjin pelaksanaan JavaScript arus perdana Node.js dan kebanyakan penyemak imbas kini menggunakan V8 sebagai enjin JavaScript. Fungsi kompilasi V8 menggunakan kompilasi tepat dalam masa, juga dikenali sebagai terjemahan dinamik atau kompilasi masa jalan, yang merupakan kaedah melaksanakan kod komputer yang melibatkan kompilasi semasa pelaksanaan program (semasa pelaksanaan) dan bukannya sebelum pelaksanaan .

Enjin V8 mempunyai had atas memori Had atas ialah 1.5G di bawah sistem pengendalian 64-bit, dan had atas ialah 800 MB di bawah sistem pengendalian 32-bit. Mengenai mengapa had atas memori ditetapkan, sebab utama ialah enjin kandungan V8 terutamanya disediakan untuk penyemak imbas dan tidak sesuai untuk ruang yang besar ialah pengumpulan sampah bersaiz ini sangat pantas, dan pengguna mempunyai hampir tiada perasaan, dengan itu meningkatkan pengalaman pengguna.

Strategi kutipan sampah V8

Enjin V8 mengamalkan idea kitar semula generasi, yang membahagikan ingatan kita kepada dua kategori mengikut peraturan tertentu, satu ialah kawasan simpanan generasi baharu, dan yang lain ialah Ia adalah kawasan simpanan generasi lama.

Objek dalam generasi baharu ialah objek dengan masa kelangsungan hidup yang singkat, ia adalah objek yang baru dijana biasanya hanya menyokong kapasiti tertentu (32 MB untuk sistem pengendalian 64-bit dan 16 MB untuk 32-. Sistem operasi bit). Objek dalam generasi lama adalah objek yang mempunyai peristiwa kelangsungan hidup yang lama atau bermastautin dalam ingatan Ringkasnya, ia adalah objek yang telah bertahan selepas pengumpulan sampah generasi baru, dan kapasitinya biasanya agak besar.

Gambar berikut menunjukkan memori dalam V8:

Enjin V8 akan menggunakan GC yang berbeza mengikut objek yang berbeza . Algoritma, algoritma GC yang biasa digunakan dalam V8 adalah seperti berikut:

  • Kitar Semula Generasi
  • Salinan Ruang
  • Pembersihan Tanda
  • Pembersihan Tanda
  • Tambahan tanda

Pengumpulan sampah objek generasi baharu

Seperti yang kami perkenalkan di atas, generasi baharu menyimpan objek dengan masa kemandirian yang lebih singkat. Proses kitar semula objek generasi baharu menggunakan algoritma salinan dan algoritma pengisihan tanda.

Algoritma penyalinan membahagikan kawasan memori generasi baharu kami kepada dua ruang dengan saiz yang sama Kami memanggil ruang keadaan yang sedang digunakan sebagai keadaan Dari, dan ruang keadaan ruang dipanggil To state,

Seperti yang ditunjukkan dalam rajah di bawah:

Kami menyimpan semua objek aktif dalam ruang Dari Apabila ruang hampir penuh, ia akan mencetuskan Kutipan sampah.

Pertama, objek aktif dalam generasi baharu Dari angkasa perlu ditanda dan disusun Selepas penandaan selesai, objek aktif yang ditanda akan disalin ke ruang Kepada dan objek yang tidak ditanda akan dikitar semula , yang Dari angkasa dan Ke angkasa untuk pertukaran.

Satu lagi perkara yang perlu dinyatakan ialah apabila menyalin objek, objek generasi baharu akan dipindahkan ke objek generasi lama.

Objek yang dialihkan ini mempunyai syarat yang ditetapkan Terdapat dua jenis utama:

    Objek generasi baharu yang bertahan dalam satu pusingan pengumpulan sampah akan dipindahkan ke yang lama. objek generasi
  • Jika penggunaan ruang Kepada melebihi 25%, objek juga akan dialihkan ke objek generasi lama (25% kerana ia takut menjejaskan peruntukan memori seterusnya)
Dapat dilihat kaedah kutipan sampah untuk objek generasi baru adalah menukar ruang dengan masa.

Pengumpulan sampah objek generasi lama

Objek yang disimpan di kawasan generasi lama adalah objek yang mempunyai masa hidup yang lama dan menempati ruang yang besar. Ia adalah tepat kerana masa kelangsungan hidup yang panjang dan pendudukan ruang yang besar bahawa algoritma replikasi tidak boleh digunakan Jika algoritma replikasi digunakan, ia akan menyebabkan masa yang lama dan pembaziran ruang.

Objek generasi lama biasanya menggunakan pembersihan tanda, pengisihan tanda dan algoritma penandaan tambahan untuk pengumpulan sampah.

Dalam fasa pembersihan, algoritma pembersihan tanda digunakan terutamanya untuk kitar semula Selepas satu tempoh masa, sejumlah besar serpihan memori terputus akan dihasilkan Apabila terlalu banyak serpihan tidak dapat memperuntukkan memori yang mencukupi, pengisihan tanda akan digunakan.

Kutipan sampah objek generasi lama akan menggunakan algoritma penandaan tambahan untuk mengoptimumkan proses pengumpulan sampah Algoritma penandaan tambahan ditunjukkan dalam rajah di bawah:

<.>

Memandangkan JavaScript adalah satu utas, hanya satu pelaksanaan program dan pengumpulan sampah boleh dijalankan pada masa yang sama Ini akan menyebabkan program membeku apabila kutipan sampah dilaksanakan, yang pastinya akan memberikan pengalaman buruk kepada pengguna.

Jadi penandaan tambahan dicadangkan Apabila program berjalan, program mula-mula berjalan untuk tempoh masa, dan kemudian melakukan penandaan awal ini hanya boleh menandakan objek yang boleh dicapai secara langsung, dan kemudian program terus berjalan untuk satu tempoh masa, penandaan tambahan sedang dilakukan, iaitu, objek yang boleh dicapai secara tidak langsung ditanda. Ulangi ini sehingga tamat.

Alat Prestasi

Memandangkan JavaScript tidak memberikan kami API untuk mengendalikan memori, kami hanya boleh bergantung pada pengurusan memori yang disediakan dengan sendirinya, tetapi kami tidak tahu apakah pengurusan memori sebenar suka. Kadangkala kita perlu memerhatikan penggunaan memori Alat Prestasi menyediakan pelbagai cara untuk memantau memori.

Langkah penggunaan prestasi

Mula-mula kami membuka penyemak imbas Chrome (di sini kami menggunakan penyemak imbas Chrome, pelayar lain juga boleh), masukkan alamat sasaran kami dalam bar alamat, dan kemudian Buka alat pembangun dan pilih panel [Prestasi].

Pilih panel prestasi dan hidupkan fungsi rakaman, kemudian akses antara muka tertentu, tiru pengguna untuk melakukan beberapa operasi, kemudian hentikan rakaman, dan akhirnya kita boleh menganalisis maklumat memori yang dirakam dalam antara muka analisis.

Manifestasi masalah ingatan

Masalah ingatan terutamanya mempunyai simptom berikut:

  • Halaman terlewat dimuatkan atau kerap dijeda , lapisan bawahnya disertai dengan pelaksanaan pengumpulan sampah yang kerap; mengapa kutipan sampah yang kerap dilakukan Mungkin ada kod secara langsung menyebabkan memori penuh dan kutipan sampah perlu dilakukan segera.

Berkenaan masalah ini, kita boleh menganalisis sebabnya melalui graf perubahan memori:

  • Halaman tersebut terus mengalami prestasi yang lemah, iaitu untuk katakan Dalam proses menggunakannya, kami merasakan bahawa halaman itu sentiasa sukar untuk digunakan Kami biasanya berpendapat bahawa akan ada pengembangan memori di bahagian bawahnya. Apa yang dipanggil pengembangan memori ialah halaman semasa digunakan untuk Yuanda untuk mencapai kelajuan tertentu Disebabkan memori yang diperlukan dengan sendirinya, memori yang diminta melebihi saiz yang boleh diberikan oleh peranti kami pada masa ini, kami dapat merasakan pengalaman prestasi yang lemah.

Masalah yang menyebabkan pengembangan memori mungkin merupakan masalah dengan kod kami, atau mungkin peranti itu sendiri lemah Jika kami ingin menganalisis, mencari dan menyelesaikannya, kami perlu menjalankan ujian berulang pada berbilang peranti

  • Prestasi halaman semakin teruk dari semasa ke semasa, dan masa pemuatan semakin lama Punca masalah ini mungkin disebabkan oleh kod Kebocoran memori .

Untuk mengesan sama ada terdapat kebocoran memori, kita boleh memantau memori kita melalui Memory Total View Jika ingatan terus meningkat, kebocoran memori mungkin telah berlaku.

Cara memantau memori

Terdapat terutamanya cara berikut untuk memantau memori dalam penyemak imbas:

  • Pengurusan tugas yang disediakan oleh penyemak imbas Peranti
  • Rajah jujukan garis masa
  • Carian petikan timbunan dipisahkan DOM
  • Tentukan sama ada terdapat kutipan sampah yang kerap

Seterusnya kami akan menerangkan perkara ini secara berasingan Beberapa cara.

Pengurus Tugas memantau memori

Tekan kekunci [Shift] [ESC] dalam penyemak imbas untuk membuka pengurus tugas yang disediakan oleh penyemak imbas Rajah berikut menunjukkan tugas dalam Pengurus penyemak imbas Chrome. mari kita tafsirkan

Dalam gambar di atas, kita dapat melihat bahawa [Memory Occupied Space] tab [Nuggets] mewakili memori yang diduduki oleh DOM halaman ini dalam penyemak imbas Jika ia berterusan Bertambah bermakna DOM baharu sedang dibuat; dan [Memori yang digunakan oleh JavaScript] berikut (tidak didayakan secara lalai, perlu dibuka dengan mengklik kanan) mewakili timbunan dalam JavaScript dan saiz dalam kurungan mewakili semua objek yang boleh dicapai dalam JavaScript .

Garis masa merekodkan memori

Pengurus tugas yang disediakan dalam penyemak imbas yang diterangkan di atas hanya boleh digunakan untuk membantu kami menentukan sama ada terdapat masalah dengan halaman, tetapi ia tidak dapat mengesan masalah dengan halaman .

Garis masa ialah tab kecil dalam alat Prestasi, yang merekodkan situasi pada halaman dalam milisaat, yang boleh membantu kami mencari masalah dengan lebih mudah.

Timbunan gambar untuk mencari DOM yang dipisahkan

Timbunan gambar sangat disasarkan untuk mencari sama ada terdapat beberapa DOM yang dipisahkan dalam objek antara muka semasa Kewujudan DOM yang dipisahkan bermakna terdapat kebocoran memori.

Pertama sekali, kita perlu memikirkan beberapa keadaan DOM:

  • Pertama sekali, objek DOM wujud dalam pokok DOM, iaitu DOM biasa
  • Kemudian, jika ia tidak wujud dalam pokok DOM dan tiada rujukan JS, ia adalah objek DOM sampah dan perlu dikitar semula
  • Akhir sekali, jika ia tidak wujud dalam pepohon DOM tetapi terdapat rujukan JS, ini adalah DOM yang dipisahkan Kami perlu mengeluarkannya secara manual.

Langkah-langkah untuk mencari DOM yang dipisahkan: Buka alatan pembangun → [Panel Memori] → [Konfigurasi Pengguna] → [Dapatkan Syot Kilat] → Masukkan Detached dalam [Penapis] untuk mencari DOM yang dipisahkan

Seperti yang ditunjukkan di bawah:

Selepas mencari DOM berasingan yang dibuat, kami mencari rujukan kepada DOM dan kemudian melepaskannya.

Tentukan sama ada kutipan sampah kerap berlaku

Kerana aplikasi berhenti apabila GC berfungsi Jika kutipan sampah semasa berfungsi dengan kerap dan mengambil masa terlalu lama, ia akan menjadi sangat tidak mesra halaman . , yang akan menyebabkan aplikasi kelihatan mati dan pengguna akan melihat aplikasi tersekat semasa digunakan.

Kita boleh menilai sama ada terdapat kutipan sampah yang kerap dengan cara berikut, seperti berikut:

  • Hakim melalui Rajah jujukan garis masa . Pantau arah aliran memori dalam panel prestasi semasa Jika terdapat peningkatan dan penurunan yang kerap, pengumpulan sampah kerap berlaku. Pada masa ini, anda perlu mencari kod untuk melihat apa yang menyebabkan keadaan ini semasa melaksanakan.
  • Adalah lebih mudah untuk menggunakan Pengurus Tugas Penyemak Imbas Perubahan utama dalam pengurus tugas adalah nilai berangka yang kerap meningkat dan berkurangan serta-merta, serta pengumpulan sampah yang kerap.

[Cadangan berkaitan: tutorial video javascript, bahagian hadapan web]

Atas ialah kandungan terperinci Pemahaman mendalam tentang pengurusan memori JavaScript dan algoritma GC. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:jb51.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam