kryo java_优化Java序列化– Java,XML,JSON,Kryo,POF
kryo java
也許我很天真,但是我一直認(rèn)為Java序列化肯定是將Java對象序列化為二進(jìn)制形式的最快,最有效的方法。 畢竟Java是第7個主要發(fā)行版,所以這不是新技術(shù),而且由于每個JDK似乎都比上一個快,因此我錯誤地認(rèn)為序列化現(xiàn)在必須非常快速和高效。 我認(rèn)為,由于Java序列化是二進(jìn)制的,并且依賴于語言,因此它必須比XML或JSON更快,更高效。 不幸的是,我錯了,如果您擔(dān)心性能,建議不要使用Java序列化。
現(xiàn)在,請不要誤會我的意思,我不是在嘗試破壞Java。 Java序列化有許多要求,主要需求是能夠?qū)⑷魏螙|西(或至少任何實(shí)現(xiàn)Serializable東西) Serializable到任何其他JVM(甚至是不同的JVM版本/實(shí)現(xiàn))中,甚至運(yùn)行要序列化的類的不同版本(例如只要您設(shè)置了serialVersionUID )。 最主要的是它可以正常工作,而且確實(shí)很棒。 性能不是主要要求,并且格式是標(biāo)準(zhǔn)的并且必須向后兼容,因此優(yōu)化非常困難。 同樣,對于許多類型的用例,Java序列化執(zhí)行得很好。
在研究三層并發(fā)基準(zhǔn)時,我開始了進(jìn)入序列化過程的旅程。 我注意到Java序列化過程中花費(fèi)了大量CPU時間,因此我決定進(jìn)行調(diào)查。 我從序列化具有幾個字段的簡單Order對象開始。 我序列化了對象并輸出了字節(jié)。 盡管Order對象只有幾個字節(jié)的數(shù)據(jù),但我并不是天真地認(rèn)為它將序列化為幾個字節(jié),但我對序列化足夠了解,因此至少需要寫出完整的類名,因此它知道它已序列化的內(nèi)容,因此可以將其讀回。 因此,我期望大約50個字節(jié)。 結(jié)果超過了600個字節(jié),那時候我意識到Java序列化并不像我想象的那么簡單。
Order對象的Java序列化字節(jié)
----sr--model.Order----h#-----J--idL--customert--Lmodel/Customer;L--descriptiont--Ljava/lang/String;L--orderLinest--Ljava/util/List;L--totalCostt--Ljava/math/BigDecimal;xp--------ppsr--java.util.ArrayListx-----a----I--sizexp----w-----sr--model.OrderLine--&-1-S----I--lineNumberL--costq-~--L--descriptionq-~--L--ordert--Lmodel/Order;xp----sr--java.math.BigDecimalT--W--(O---I--scaleL--intValt--Ljava/math/BigInteger;xr--java.lang.Number-----------xp----sr--java.math.BigInteger-----;-----I--bitCountI--bitLengthI--firstNonzeroByteNumI--lowestSetBitI--signum[--magnitudet--[Bxq-~----------------------ur--[B------T----xp----xxpq-~--xq-~--(注意“-”表示不可打印的字符)
您可能已經(jīng)注意到,Java序列化不僅寫出要序列化的對象的完整類名,而且還寫出要序列化的類的整個類定義以及所有引用的類。 類定義可能非常大,并且似乎是主要的性能和效率問題,尤其是在編寫單個對象時。 如果要寫出大量相同類的對象,則類定義開銷通常不是大問題。 我注意到的另一件事是,如果您的對象具有對類的引用(例如元數(shù)據(jù)對象),則Java序列化將編寫整個類定義,而不僅僅是類名,因此使用Java序列化來編寫元數(shù)據(jù)非常昂貴。
可外部化
通過實(shí)現(xiàn)Externalizable接口可以優(yōu)化Java序列化。 實(shí)現(xiàn)此接口可以避免寫出整個類定義,而只需編寫類名即可。 它要求您實(shí)現(xiàn)readExternal和writeExternal方法,因此需要您進(jìn)行一些工作和維護(hù),但是比僅實(shí)現(xiàn)Serializable可更快,更有效。
關(guān)于Externalizable結(jié)果的一個有趣注釋是,對于少量對象,它的效率要高得多,但對于大量對象,實(shí)際上輸出的字節(jié)數(shù)要比Serializable多得多。 我假設(shè)Externalizable格式對重復(fù)對象的效率稍低。
可外部化的類
public class Order implements Externalizable {private long id;private String description;private BigDecimal totalCost = BigDecimal.valueOf(0);private List orderLines = new ArrayList();private Customer customer;public Order() {}public void readExternal(ObjectInput stream) throws IOException, ClassNotFoundException {this.id = stream.readLong();this.description = (String)stream.readObject();this.totalCost = (BigDecimal)stream.readObject();this.customer = (Customer)stream.readObject();this.orderLines = (List)stream.readObject();}public void writeExternal(ObjectOutput stream) throws IOException {stream.writeLong(this.id);stream.writeObject(this.description);stream.writeObject(this.totalCost);stream.writeObject(this.customer);stream.writeObject(this.orderLines);} }Order對象的可外部化的序列化字節(jié)
----sr--model.Order---*3--^---xpw---------psr--java.math.BigDecimalT--W--(O---I--scaleL--intValt--Ljava/math/BigInteger;xr--java.lang.Number-----------xp----sr--java.math.BigInteger-----;-----I--bitCountI--bitLengthI--firstNonzeroByteNumI--lowestSetBitI--signum[--magnitudet--[Bxq-~----------------------ur--[B------T----xp----xxpsr--java.util.ArrayListx-----a----I--sizexp----w-----sr--model.OrderLine-!!|---S---xpw-----pq-~--q-~--xxx其他序列化選項
我開始研究Java中還有哪些其他序列化選項。 我從EclipseLink MOXy開始,它支持通過JAXB API將對象序列化為XML或JSON。 我并不期望XML序列化能勝過Java序列化,因此在某些用例中確實(shí)感到驚訝。 我還找到了產(chǎn)品Kryo,這是一個用于優(yōu)化序列化的開源項目。 我還研究了Oracle Coherence POF序列化格式。 每個產(chǎn)品都有優(yōu)點(diǎn)和缺點(diǎn),但我的主要重點(diǎn)是比較它們的性能和效率。
EclipseLink MOXy – XML和JSON
使用EclipseLink MOXy序列化為XML或JSON的主要優(yōu)點(diǎn)是兩者都是標(biāo)準(zhǔn)的可移植格式。 您可以使用任何語言從任何客戶端訪問數(shù)據(jù),因此與Java序列化一樣,不限于Java。 您還可以將數(shù)據(jù)與Web服務(wù)和REST服務(wù)集成。 兩種格式都基于文本,因此易于閱讀。 不需要編碼或特殊接口,只需元數(shù)據(jù)。 性能是完全可以接受的,并且對于小型數(shù)據(jù)集,其性能優(yōu)于Java序列化。
缺點(diǎn)是文本格式的效率不如優(yōu)化的二進(jìn)制格式,并且JAXB需要元數(shù)據(jù)。 因此,您需要使用JAXB批注對類進(jìn)行批注,或者提供XML配置文件。 另外,默認(rèn)情況下不處理循環(huán)引用,您需要使用@XmlIDREF來處理循環(huán)。
JAXB注釋的類
@XmlRootElement public class Order {@XmlID@XmlAttributeprivate long id;@XmlAttributeprivate String description;@XmlAttributeprivate BigDecimal totalCost = BigDecimal.valueOf(0);private List orderLines = new ArrayList();private Customer customer; }public class OrderLine {@XmlIDREFprivate Order order;@XmlAttributeprivate int lineNumber;@XmlAttributeprivate String description;@XmlAttributeprivate BigDecimal cost = BigDecimal.valueOf(0); }訂單對象的EclipseLink MOXy序列化XML
<order id="0" totalCost="0"><orderLines lineNumber="1" cost="0"><order>0</order></orderLines></order>訂單對象的EclipseLink MOXy序列化JSON
{"order":{"id":0,"totalCost":0,"orderLines":[{"lineNumber":1,"cost":0,"order":0}]}}ry
Kryo是一個快速,高效的Java序列化框架。 Kryo是根據(jù)New BSD許可提供的有關(guān)Google代碼的開源項目。 這是一個只有3個成員的小型項目,它于2009年首次發(fā)布,最后一次于2013年2月發(fā)布2.21版本,因此仍在積極開發(fā)中。
Kryo的工作方式類似于Java序列化,并且尊重瞬態(tài)字段,但不需要類可序列化。 我發(fā)現(xiàn)Kryo有一些限制,例如要求類具有默認(rèn)構(gòu)造函數(shù),并且在序列化java.sql.Time,java.sql.Date和java.sql.Timestamp類時遇到了一些問題。
Order對象的Kryo序列化字節(jié)
------java-util-ArrayLis-----model-OrderLin----java-math-BigDecima---------model-Orde-----Oracle Coherence POF
Oracle Coherence產(chǎn)品提供了自己的優(yōu)化二進(jìn)制格式,稱為POF(便攜式對象格式)。 Oracle Coherence是一種內(nèi)存數(shù)據(jù)網(wǎng)格解決方案(分布式緩存)。 一致性是一種商業(yè)產(chǎn)品,需要許可證。 EclipseLink通過使用Coherence作為EclipseLink共享緩存的Oracle TopLink Grid產(chǎn)品支持與Oracle Coherence的集成。
POF提供了序列化框架,可以獨(dú)立于Coherence使用(如果您已經(jīng)獲得Coherence許可)。 POF要求您的類實(shí)現(xiàn)PortableObject接口和讀/寫方法。 您還可以實(shí)現(xiàn)單獨(dú)的Serializer類,或在最新的Coherence版本中使用注釋。 POF要求每個類提前分配一個常量ID,因此您需要某種方式確定此ID。 POF格式是一種二進(jìn)制格式,非常緊湊,高效且快速,但是您需要做一些工作。
對于單個Order / OrderLine對象,POF的總字節(jié)為32個字節(jié),對于100個OrderLines,則為1593個字節(jié)。 我不會給出結(jié)果,因?yàn)镻OF是一種商業(yè)許可產(chǎn)品的一部分,但是速度非常快。
POF便攜式對象
public class Order implements PortableObject {private long id;private String description;private BigDecimal totalCost = BigDecimal.valueOf(0);private List orderLines = new ArrayList();private Customer customer;public Order() {}public void readExternal(PofReader in) throws IOException {this.id = in.readLong(0);this.description = in.readString(1);this.totalCost = in.readBigDecimal(2);this.customer = (Customer)in.readObject(3);this.orderLines = (List)in.readCollection(4, new ArrayList());}public void writeExternal(PofWriter out) throws IOException {out.writeLong(0, this.id);out.writeString(1, this.description);out.writeBigDecimal(2, this.totalCost);out.writeObject(3, this.customer);out.writeCollection(4, this.orderLines);} }Order對象的POF序列化字節(jié)
-----B--G---d-U------A--G-------結(jié)果
那么每種表現(xiàn)如何呢? 我做了一個簡單的基準(zhǔn)比較不同的序列化機(jī)制。 我比較了兩個不同用例的序列化。 第一個是具有單個OrderLine對象的單個Order對象。 第二個是具有100個OrderLine對象的單個Order對象。 我比較了每秒的平均序列化操作,并測量了序列化數(shù)據(jù)的字節(jié)大小。 不同的對象模型,用例和環(huán)境將產(chǎn)生不同的結(jié)果,但這使您對不同的序列化器的性能差異有一個大致的了解。
結(jié)果表明,對于少量對象,Java序列化速度較慢,但??對于大量對象,Java序列化速度較好。 相反,對于少量對象,XML和JSON的性能優(yōu)于Java序列化,但是對于大量對象,Java序列化的速度更快。 Kryo和其他優(yōu)化的二進(jìn)制序列化程序在這兩種類型的數(shù)據(jù)上均優(yōu)于Java序列化。
您可能想知道,為什么不到一毫秒的時間與性能有任何關(guān)系是有意義的,而您可能是對的。 通常,如果您寫出大量對象,然后Java序列化執(zhí)行得很好,那么您只會遇到一個實(shí)際的性能問題,那么,對于少量對象而言,它的執(zhí)行效果很差嗎? 對于單個操作,這可能是正確的,但是如果執(zhí)行許多小的序列化操作,則成本是相關(guān)的。 為許多客戶端提供服務(wù)的典型服務(wù)器通常會發(fā)出許多小請求,因此,盡管序列化的成本不足以使這些單個請求中的任何一個花費(fèi)很長時間,但它將極大地影響服務(wù)器的可伸縮性。
用1條訂單行訂購
| Java可序列化 | 636 | 128,634 | 19,180 | 0% | 0% |
| Java可外部化 | 435 | 160,549 | 26,678 | 24% | 39% |
| EclipseLink MOXy XML | 101 | 348,056 | 47,334 | 170% | 146% |
| ry | 90 | 359,368 | 346,984 | 179% | 1709% |
訂購100條訂單行
| Java可序列化 | 2,715 | 16,470 | 10,215 | 0% | 0% |
| Java可外部化 | 2,811 | 16,206 | 11,483 | -1% | 12% |
| EclipseLink MOXy XML | 6,628 | 7,304 | 2,731 | -55% | -73% |
| ry | 1216 | 22862 | 31,499 | 38% | 208% |
EclipseLink JPA
在EclipseLink 2.6開發(fā)版本(在某種程度上為2.5)中,我們增加了在EclipseLink進(jìn)行序列化的任何地方選擇序列化程序的功能。
這樣的地方之一就是序列化@Lob映射。 現(xiàn)在,您可以使用@Convert批注指定序列化程序,例如@Convert(XML),@ Convert(JSON),@ Convert(Kryo)。 除了優(yōu)化性能之外,這還提供了一種簡單的機(jī)制將XML和JSON數(shù)據(jù)寫入數(shù)據(jù)庫。
同樣對于EclipseLink緩存協(xié)調(diào),您可以使用“ eclipselink.cache.coordination.serializer”屬性選擇序列化器。
這篇文章中使用的基準(zhǔn)測試的源代碼可以在這里找到,或者在這里下載。
翻譯自: https://www.javacodegeeks.com/2013/09/optimizing-java-serialization-java-vs-xml-vs-json-vs-kryo-vs-pof.html
kryo java
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的kryo java_优化Java序列化– Java,XML,JSON,Kryo,POF的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑蓝屏怎么修复电脑如何修复蓝屏
- 下一篇: 创新设计模式:工厂模式