Saya seorang insinyur perangkat lunak dan setelah berdiskusi dengan beberapa rekan, saya menyadari bahwa saya tidak memiliki pemahaman yang baik tentang konsep serialisasi. Seperti yang saya mengerti, serialisasi adalah proses mengubah beberapa entitas, seperti objek dalam OOP, ke urutan byte, sehingga entitas tersebut dapat disimpan atau dikirim untuk akses selanjutnya (proses "deserialisasi").
Masalah yang saya miliki adalah: bukankah semua variabel (baik itu primitif int
atau objek komposit) sudah diwakili oleh urutan byte? (Tentu saja, karena disimpan dalam register, memori, disk, dll.)
Jadi apa yang membuat serialisasi menjadi topik yang mendalam? Untuk membuat serial suatu variabel, tidak bisakah kita mengambil byte ini dalam memori, dan menulisnya ke file? Seluk beluk apa yang saya lewatkan?
4 bytes
pada PDP-11 saya dan kemudian mencoba dan membaca empat byte yang sama ke dalam memori di macbook saya mereka bukan nomor yang sama (karena Endianes). Jadi, Anda harus menormalkan data ke representasi yang dapat Anda hapus kode (ini adalah serialisasi). Bagaimana Anda membuat serialisasi data juga memiliki pengorbanan kecepatan / fleksibilitas yang dapat dibaca manusia / mesin.Jawaban:
Jika Anda memiliki struktur data yang rumit, perwakilannya dalam memori biasanya tersebar di seluruh memori. (Pikirkan pohon biner, misalnya.)
Sebaliknya, ketika Anda ingin menulisnya ke disk, Anda mungkin ingin memiliki representasi sebagai urutan (mudah-mudahan pendek) byte yang berdekatan. Itulah yang dilakukan serialisasi untuk Anda.
sumber
Pertimbangkan grafik objek dalam C dengan node yang didefinisikan sebagai ini:
Pada saat runtime, seluruh
Node
grafik objek akan tersebar di sekitar ruang memori, dan simpul yang sama dapat diarahkan ke banyak Node yang berbeda.Anda tidak bisa begitu saja membuang memori ke file / stream / disk dan menyebutnya serialized karena nilai pointer (yang merupakan alamat memori) tidak dapat di-serialkan (karena lokasi memori tersebut mungkin sudah ditempati ketika Anda memuat dump kembali ke dalam memori). Masalah lain dengan hanya membuang memori adalah bahwa Anda pada akhirnya akan menyimpan semua jenis data yang tidak relevan dan ruang yang tidak digunakan - pada x86 sebuah proses memiliki ruang memori hingga 4Gb, dan OS atau MMU hanya memiliki gambaran umum tentang apa sebenarnya memori bermakna atau tidak (berdasarkan halaman memori yang ditugaskan untuk suatu proses), jadi memiliki
Notepad.exe
dump 4GB byte mentah ke disk saya setiap kali saya ingin menyimpan file teks sepertinya agak boros.Masalah lain adalah dengan versi: apa yang terjadi jika Anda membuat serial
Node
grafik Anda pada hari 1, kemudian pada hari 2 Anda menambahkan bidang lain keNode
(seperti nilai pointer lain, atau nilai primitif), maka pada hari ke 3 Anda membatalkan serialisasi file Anda dari hari 1?Anda juga harus mempertimbangkan hal-hal lain, seperti endianness. Salah satu alasan utama mengapa file MacOS dan IBM / Windows / PC tidak kompatibel satu sama lain pada 1980-an dan 1990-an meskipun seolah-olah sedang dibuat oleh program yang sama (Word, Photoshop, dll) adalah karena nilai integer x86 / PC multi-byte multi-byte disimpan dalam urutan little-endian, tetapi pesanan big-endian pada Mac - dan perangkat lunak tidak dibuat dengan mempertimbangkan portabilitas lintas-platform. Saat ini segalanya menjadi lebih baik berkat peningkatan pendidikan pengembang dan dunia komputasi kami yang semakin heterogen.
sumber
Caranya sebenarnya sudah dijelaskan dalam kata itu sendiri: " serial ization".
Pertanyaannya pada dasarnya adalah: bagaimana saya bisa mewakili grafik yang diarahkan kompleks kompleks yang saling terhubung secara arbitrer dari objek kompleks semena-mena sebagai urutan linear byte?
Pikirkan tentang hal ini: urutan linier adalah seperti grafik terarah degenerasi di mana setiap titik memiliki tepat satu tepi masuk dan keluar (kecuali "simpul pertama" yang tidak memiliki tepi masuk dan "simpul terakhir" yang tidak memiliki tepi keluar) . Dan byte jelas kurang kompleks daripada objek .
Jadi, tampaknya masuk akal bahwa ketika kita beralih dari grafik yang kompleks sewenang-wenang ke "grafik" yang jauh lebih terbatas (sebenarnya hanya sebuah daftar) dan dari objek yang kompleks semena-mena ke byte sederhana, informasi akan hilang, jika kita melakukan ini secara naif dan tidak t menyandikan informasi "asing" dengan cara tertentu. Dan itulah yang dilakukan serialisasi: menyandikan informasi kompleks ke dalam format linear sederhana.
Jika Anda terbiasa dengan YAML , Anda mungkin harus melihat fitur anchor dan alias yang memungkinkan Anda untuk mewakili gagasan bahwa "objek yang sama dapat muncul di tempat yang berbeda" dalam serialisasi.
Misalnya jika Anda memiliki grafik berikut:
Anda dapat menyatakan bahwa sebagai daftar jalur linear di YAML seperti ini:
Anda juga bisa mewakilinya sebagai daftar adjacency, atau matriks adjacency, atau sebagai pasangan yang elemen pertamanya adalah serangkaian node dan yang elemen keduanya adalah kumpulan pasangan node, tetapi dalam semua representasi itu, Anda harus memiliki cara merujuk mundur dan maju ke node yang ada , yaitu pointer , yang biasanya tidak Anda miliki dalam file atau aliran jaringan. Yang Anda miliki, pada akhirnya, adalah byte.
(Yang BTW berarti bahwa file teks YAML di atas itu sendiri juga perlu "serial", itulah yang digunakan untuk berbagai pengkodean karakter dan format transfer Unicode ... itu bukan "serialisasi" semata, hanya pengkodean, karena file teks sudah menjadi serial / Daftar linear dari codepoint, tetapi Anda dapat melihat beberapa kesamaan.)
sumber
Jawaban lain sudah membahas grafik objek yang kompleks, tetapi ada baiknya menunjukkan bahwa serialisasi primitif juga non-sepele.
Menggunakan nama tipe C primitif untuk konkret, pertimbangkan:
Saya membuat serial a
long
. Beberapa waktu kemudian saya de-cerita bersambung, tapi ... pada platform yang berbeda, dan sekaranglong
adalahint64_t
daripadaint32_t
saya disimpan. Jadi, saya harus sangat berhati-hati tentang ukuran yang tepat dari setiap jenis yang saya simpan, atau menyimpan beberapa metadata yang menjelaskan jenis dan ukuran setiap bidang.Perhatikan bahwa platform yang berbeda ini bisa saja platform yang sama setelah kompilasi ulang di masa depan.
Saya membuat serial sebuah
int32_t
. Beberapa waktu kemudian saya membatalkan serialisasi, tapi ... pada platform yang berbeda, dan sekarang nilainya rusak. Sayangnya saya menyimpan nilai pada platform big-endian, dan memuatnya pada little-endian. Sekarang saya perlu membuat konvensi untuk format saya, atau menambahkan lebih banyak metadata yang menggambarkan endiannness dari setiap file / stream / apa pun. Dan, tentu saja, benar-benar melakukan konversi yang sesuai.char
dan UTF-8, dan satuwchar_t
dan UTF-16.Jadi, saya akan mengklaim bahwa serialisasi berkualitas wajar tidak sepele bahkan untuk primitif dalam memori yang berdekatan. Ada banyak keputusan penyandian yang perlu Anda dokumentasikan, atau uraikan dengan metadata sebaris.
Grafik objek menambahkan lapisan kompleksitas lainnya di atas itu.
sumber
Ada beberapa aspek:
Keterbacaan oleh program yang sama
Program Anda entah bagaimana menyimpan data Anda sebagai byte dalam memori. Tapi itu mungkin secara sewenang-wenang tersebar di register yang berbeda, dengan pointer bolak-balik di antara bagian-bagian yang lebih kecil [sunting: Seperti dikomentari, secara fisik data lebih mungkin di memori utama daripada register data, tetapi itu tidak menghilangkan masalah pointer] . Pikirkan saja daftar bilangan bulat yang ditautkan. Setiap elemen daftar dapat disimpan di tempat yang sama sekali berbeda dan semua yang menyatukan daftar adalah petunjuk dari satu elemen ke elemen berikutnya. Jika Anda mengambil data apa adanya dan mencoba menyalinnya di komputer lain yang menjalankan program yang sama, Anda akan mengalami masalah:
Keterbacaan oleh program lain
Katakanlah Anda berhasil mengalokasikan alamat yang tepat di komputer lain, agar data Anda cocok. Jika data Anda diproses oleh program terpisah pada mesin itu (bahasa yang berbeda), program itu mungkin memiliki pemahaman dasar data yang sama sekali berbeda. Katakanlah Anda memiliki objek C ++ dengan pointer, tetapi bahasa target Anda bahkan tidak mendukung pointer pada level itu. Sekali lagi, Anda berakhir dengan tidak ada cara yang bersih untuk menangani data itu dalam program kedua. Anda berakhir dengan beberapa data biner dalam memori, tetapi kemudian, Anda perlu menulis kode tambahan yang membungkus data dan entah bagaimana menerjemahkannya menjadi sesuatu yang dapat digunakan oleh bahasa target Anda. Kedengarannya seperti deserialization, hanya saja titik awal Anda sekarang adalah benda aneh yang tersebar di sekitar memori utama Anda, yang berbeda untuk bahasa sumber yang berbeda, bukannya file dengan struktur yang terdefinisi dengan baik. Hal yang sama, tentu saja, jika Anda mencoba untuk secara langsung menginterpretasikan file biner yang mencakup pointer - Anda perlu menulis parser untuk setiap cara yang mungkin bahasa lain mewakili data dalam memori.
Keterbacaan oleh manusia
Dua bahasa serialisasi modern yang paling menonjol untuk serialisasi berbasis web (xml, json) mudah dimengerti oleh manusia. Alih-alih tumpukan biner dari goo, struktur aktual dan konten data jelas bahkan tanpa program untuk membaca data. Ini memiliki beberapa keunggulan:
sumber
Selain apa yang dikatakan jawaban lain:
Terkadang Anda ingin membuat serial hal-hal yang bukan data murni.
Sebagai contoh, pikirkan file handle atau koneksi ke server. Meskipun pegangan file atau soketnya adalah
int
, angka ini tidak berarti apa-apa saat berikutnya program dijalankan. Untuk membuat ulang objek dengan benar yang berisi pegangan untuk hal-hal seperti itu, Anda perlu membuka kembali file dan membuat ulang koneksi, dan memutuskan apa yang harus dilakukan jika ini gagal.Banyak bahasa saat ini mendukung penyimpanan fungsi anonim dalam objek, misalnya
onBlah()
penangan dalam Javascript. Ini menantang karena kode tersebut dapat berisi referensi ke potongan data tambahan yang pada gilirannya perlu diserialisasi. (Dan kemudian ada masalah kode serialisasi dalam cara lintas-platform, yang jelas lebih mudah untuk bahasa yang ditafsirkan.) Namun, bahkan jika hanya sebagian dari bahasa yang dapat didukung, masih dapat terbukti sangat berguna. Tidak banyak mekanisme serialisasi yang mencoba kode serialisasi, tetapi lihat serialize-javascript .Dalam kasus di mana Anda ingin membuat serial objek, tetapi berisi sesuatu yang tidak didukung oleh mekanisme serialisasi Anda, Anda perlu menulis ulang kode dengan cara yang bekerja di sekitar ini. Misalnya, Anda dapat menggunakan enum sebagai pengganti fungsi anonim saat ada sejumlah fungsi yang mungkin.
Seringkali Anda ingin data berseri menjadi singkat.
Jika Anda mengirim data melalui jaringan atau bahkan menyimpannya di disk, penting untuk menjaga ukurannya tetap kecil. Salah satu cara termudah untuk mencapai ini adalah membuang informasi yang dapat dibangun kembali (misalnya, membuang cache, tabel hash, dan representasi alternatif dari data yang sama).
Tentu saja, programmer harus memilih secara manual apa yang akan disimpan dan apa yang harus dibuang, dan memastikan semuanya dibangun kembali ketika objek diciptakan kembali.
Pikirkan tentang tindakan menyelamatkan permainan. Objek mungkin mengandung banyak pointer ke data grafik, data suara, dan objek lainnya. Tetapi sebagian besar hal ini dapat dimuat dari file data game dan tidak perlu disimpan dalam file save. Membuangnya bisa melelahkan sehingga hal-hal kecil sering ditinggalkan. Saya telah mengedit beberapa file yang disimpan di waktu saya dan menemukan data yang jelas-jelas berlebihan, seperti deskripsi item tekstual.
Kadang-kadang ruang tidak penting tetapi keterbacaan adalah — dalam hal ini Anda mungkin menggunakan format ASCII (mungkin JSON atau XML).
sumber
Mari kita tentukan apa sebenarnya urutan byte itu. Urutan byte terdiri dari bilangan bulat non-negatif yang disebut panjang dan beberapa fungsi / korespondensi arbitrer yang memetakan sembarang bilangan bulat i yang setidaknya nol dan kurang dari panjang ke nilai byte (bilangan bulat dari 0 hingga 255).
Banyak objek yang Anda tangani dalam program tipikal tidak dalam bentuk itu, karena objek sebenarnya terdiri dari banyak alokasi memori berbeda yang berada di tempat yang berbeda dalam RAM, dan dapat dipisahkan dari satu sama lain oleh jutaan byte barang yang Anda miliki. tidak peduli. Bayangkan saja daftar tertaut dasar: setiap node dalam daftar adalah urutan byte, ya, tetapi node berada di banyak lokasi berbeda di memori komputer Anda, dan mereka terhubung dengan pointer. Atau pikirkan saja struct sederhana yang memiliki pointer ke string panjang variabel.
Alasan mengapa kami ingin membuat serial struktur data menjadi urutan byte biasanya karena kami ingin menyimpannya di disk atau mengirimnya ke sistem yang berbeda (misalnya melalui jaringan). Jika Anda mencoba untuk menyimpan pointer pada disk atau mengirimnya ke sistem yang berbeda, itu akan sangat tidak berguna karena program yang membaca pointer itu akan memiliki set area memori yang berbeda tersedia.
sumber
int seq(int i) { if (0 <= i < length) return i+1; else return -1;}
adalah urutan. Jadi bagaimana saya akan menyimpannya di disk?sin
menjadi tabel pencarian, yang merupakan urutan angka? Tahukah Anda fungsi Anda sama dengan yang ini untuk input yang kami pedulikan?int seq(n) { int a[] = [1, 2, 3, 4]; return a[n]; }
Mengapa tepatnya Anda mengatakan bahwa file empat byte saya adalah representasi yang tidak memadai?Seluk-beluk mencerminkan seluk-beluk data dan objek itu sendiri. Objek-objek ini dapat berupa objek dunia nyata, atau hanya objek komputer. Jawabannya ada dalam nama. Serialisasi adalah representasi linear dari objek multi dimensional. Ada banyak masalah selain RAM yang terfragmentasi.
Jika Anda dapat meratakan 12 array lima dimensi dan beberapa kode program, serialisasi juga memungkinkan Anda untuk mentransfer seluruh program komputer (dan data) antar mesin. Protokol komputasi terdistribusi seperti RMI / CORBA menggunakan serialisasi secara ekstensif untuk mentransfer data dan program.
Pertimbangkan tagihan telepon Anda. Mungkin objek tunggal, terdiri dari semua panggilan Anda (daftar string), jumlah yang harus dibayar (integer) dan negara. Atau tagihan telepon Anda bisa dari luar ke atas dan terdiri dari panggilan telepon terperinci diskrit yang ditautkan dengan nama Anda. Setiap bagian yang rata akan terlihat berbeda, mencerminkan bagaimana perusahaan telepon Anda menulis bahwa versi perangkat lunaknya dan alasan mengapa basis data berorientasi objek tidak pernah lepas landas.
Beberapa bagian struktur bahkan mungkin tidak ada dalam memori sama sekali. Jika Anda memiliki caching yang malas, beberapa bagian dari objek mungkin hanya dirujuk ke file disk, dan hanya dimuat ketika bagian dari objek tertentu diakses. Ini biasa terjadi dalam kerangka kegigihan yang serius. Gumpalan adalah contoh yang bagus. Getty Images dapat menyimpan gambar multimabyte besar Fidel Castro dan beberapa data meta seperti nama gambar, biaya sewa dan gambar itu sendiri. Anda mungkin tidak ingin memuat gambar 200 MB ke dalam memori setiap kali, kecuali jika Anda benar-benar melihatnya. Serialized, seluruh file akan membutuhkan lebih dari 200MB penyimpanan.
Beberapa objek bahkan tidak dapat diserialisasi sama sekali. Di tanah pemrograman Java, Anda dapat memiliki objek pemrograman yang mewakili layar grafis atau port serial fisik. Tidak ada konsep serialisasi yang nyata dari keduanya. Bagaimana Anda mengirim port Anda ke orang lain melalui jaringan?
Beberapa hal seperti kata sandi / kunci enkripsi tidak boleh disimpan atau dikirim. Mereka dapat ditandai seperti itu (volatile / transient dll) dan proses serialisasi akan melompati mereka tetapi mereka dapat hidup dalam RAM. Menghilangkan tag ini adalah bagaimana kunci enkripsi secara tidak sengaja dikirim / disimpan dalam ASCII biasa.
Ini dan jawaban lainnya adalah mengapa ini rumit.
sumber
Iya itu mereka. Masalahnya di sini adalah tata letak byte tersebut. Sederhana
int
bisa 2, 4 atau 8 bit. Bisa dalam endian besar atau kecil. Itu dapat ditandatangani, ditandatangani dengan komplemen 1 atau bahkan dalam beberapa kode bit super eksotis seperti negabinary.Jika Anda hanya membuang
int
binarily dari memori, dan menyebutnya "serial", Anda harus memasang cukup banyak seluruh komputer, sistem operasi dan program Anda agar dapat deserializable. Atau setidaknya, deskripsi yang tepat tentang mereka.Serialisasi objek sederhana cukup banyak menuliskannya menurut beberapa aturan. Aturan-aturan itu banyak dan tidak selalu jelas. Misalnya
xs:integer
dalam XML ditulis dalam basis-10. Bukan base-16, bukan base-9, tapi 10. Ini bukan asumsi tersembunyi, itu aturan yang sebenarnya. Dan aturan seperti itu membuat serialisasi menjadi serialisasi. Karena, cukup banyak, tidak ada aturan tentang tata letak bit program Anda di memori .Itu hanya puncak gunung es. Mari kita mengambil contoh dari urutan orang-orang primitif yang paling sederhana: C
struct
. Anda bisa memikirkan itumemiliki tata letak memori yang ditentukan pada komputer + OS yang diberikan? Ya tidak. Bergantung pada
#pragma pack
pengaturan saat ini , kompiler akan mengisi bidang. Pada pengaturan default kompilasi 32-bit, keduanyashorts
akan diisi hingga 4 byte sehinggastruct
sebenarnya akan memiliki 3 bidang 4 byte dalam memori. Jadi sekarang, Anda tidak hanya harus menentukan bahwashort
16 bit panjang, itu adalah bilangan bulat, ditulis dalam komplemen negatif 1, endian besar atau kecil. Anda juga harus menuliskan pengaturan struktur packing program Anda dikompilasi.Itulah yang dimaksud dengan serialisasi: membuat seperangkat aturan, dan berpegang teguh padanya.
Aturan-aturan itu kemudian dapat diperluas untuk menerima struktur yang lebih canggih (seperti daftar panjang variabel atau data nonlinear), fitur tambahan seperti keterbacaan manusia, versi, kompatibilitas mundur dan koreksi kesalahan, dll. Tetapi bahkan menulis satu pun
int
sudah cukup rumit jika Anda hanya ingin memastikan Anda dapat membacanya kembali dengan andal.sumber