Java最佳實踐–高性能序列化

在使用Java編程語言時,我們將繼續討論與建議的實踐有關的系列文章,我們將討論并演示如何將對象序列化用于高性能應用程序。

所有討論的主題均基于用例,這些用例來自于電信行業的關鍵任務超高性能生產系統的開發。

在閱讀本文的每個部分之前,強烈建議您參考相關的Java API文檔以獲取詳細信息和代碼示例。

所有測試均針對具有以下特征的Sony Vaio進行:

  • 系統:openSUSE 11.1(x86_64)
  • 處理器(CPU):Intel(R)Core(TM)2 Duo CPU T6670 @ 2.20GHz
  • 處理器速度:1,200.00 MHz
  • 總內存(RAM):2.8 GB
  • Java:OpenJDK 1.6.0_0 64位

應用以下測試配置:

  • 并發工作者線程:200
  • 每個工作人員重復測試的次數:1000
  • 整體測試次數:100

高性能序列化

序列化是將對象轉換為字節流的過程。 然后可以通過套接字發送該流,將其存儲到文件和/或數據庫中,或者直接按原樣對其進行操作。 在本文中,我們不打算對序列化機制進行深入的描述,有許多文章提供了這種信息。 這里將討論的是我們利用序列化以實現高性能結果的主張。

序列化的三個主要性能問題是:

  • 序列化是一種遞歸算法。 從單個對象開始,通過實例變量可以從該對象訪問的所有對象也被序列化。 默認行為很容易導致不必要的序列化開銷
  • 序列化和反序列化都需要序列化機制來發現有關其序列化實例的信息。 使用默認的序列化機制,將使用反射來發現所有字段值。 此外,如果您未明確設置“ serialVersionUID”類屬性,則序列化機制必須對其進行計算。 這涉及遍歷所有字段和方法以生成哈希。 上述過程可能很慢
  • 使用默認的序列化機制,所有序列化類描述信息都包含在流中,例如:
    • 所有可序列化超類的描述
    • 類本身的描述
    • 與類的特定實例相關聯的實例數據

要解決上述性能問題,可以改用外部化。 這兩種方法之間的主要區別在于,序列化將所有可序列化超類的類描述以及與該實例相關聯的信息(當被視為每個單獨的超類的實例)寫出。 另一方面,外部化將寫出類的標識(類的名稱和適當的“ serialVersionUID”類屬性)以及超類結構以及有關類層次結構的所有信息。 換句話說,它存儲所有元數據,但僅寫出本地實例信息。 簡而言之,外部化幾乎消除了序列化機制使用的所有反射調用,使您可以完全控制編組和解組算法,從而顯著提高性能。

當然,外部化效率是有代價的。 由于從類定義中自動提取了元數據,因此默認的序列化機制可適應應用程序更改。 另一方面,外部化不是很靈活,需要您在更改類定義時重寫編組和解組代碼。

以下是有關如何將外部化用于高性能應用程序的簡短演示。 我們將從提供“ Employee”對象開始執行序列化和反序列化操作。 將使用兩種類型的“ Employee”對象。 一種適合標準序列化操作,另一種經過修改以便可以外部化。

以下是“雇員”對象的第一種味道:

package com.javacodegeeks.test;import java.io.Serializable;
import java.util.Date;
import java.util.List;public class Employee implements Serializable {private static final long serialVersionUID = 3657773293974543890L;private String firstName;private String lastName;private String socialSecurityNumber;private String department;private String position;private Date hireDate;private Double salary;private Employee supervisor;private List<string> phoneNumbers;public Employee() {}public Employee(String firstName, String lastName,String socialSecurityNumber, String department, String position,Date hireDate, Double salary) {this.firstName = firstName;this.lastName = lastName;this.socialSecurityNumber = socialSecurityNumber;this.department = department;this.position = position;this.hireDate = hireDate;this.salary = salary;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getSocialSecurityNumber() {return socialSecurityNumber;}public void setSocialSecurityNumber(String socialSecurityNumber) {this.socialSecurityNumber = socialSecurityNumber;}public String getDepartment() {return department;}public void setDepartment(String department) {this.department = department;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}public Date getHireDate() {return hireDate;}public void setHireDate(Date hireDate) {this.hireDate = hireDate;}public Double getSalary() {return salary;}public void setSalary(Double salary) {this.salary = salary;}public Employee getSupervisor() {return supervisor;}public void setSupervisor(Employee supervisor) {this.supervisor = supervisor;}public List<string> getPhoneNumbers() {return phoneNumbers;}public void setPhoneNumbers(List<string> phoneNumbers) {this.phoneNumbers = phoneNumbers;}}

這里要注意的事情:

  • 我們假設以下字段是必填字段:
    • “名字”
    • “姓”
    • “社會安全號碼”
    • “部”
    • “位置”
    • “雇用日期”
    • “薪水”

以下是“雇員”對象的第二種風味:

package com.javacodegeeks.test;import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.Date;
import java.util.List;public class Employee implements Externalizable {private String firstName;private String lastName;private String socialSecurityNumber;private String department;private String position;private Date hireDate;private Double salary;private Employee supervisor;private List<string> phoneNumbers;public Employee() {}public Employee(String firstName, String lastName,String socialSecurityNumber, String department, String position,Date hireDate, Double salary) {this.firstName = firstName;this.lastName = lastName;this.socialSecurityNumber = socialSecurityNumber;this.department = department;this.position = position;this.hireDate = hireDate;this.salary = salary;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getSocialSecurityNumber() {return socialSecurityNumber;}public void setSocialSecurityNumber(String socialSecurityNumber) {this.socialSecurityNumber = socialSecurityNumber;}public String getDepartment() {return department;}public void setDepartment(String department) {this.department = department;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}public Date getHireDate() {return hireDate;}public void setHireDate(Date hireDate) {this.hireDate = hireDate;}public Double getSalary() {return salary;}public void setSalary(Double salary) {this.salary = salary;}public Employee getSupervisor() {return supervisor;}public void setSupervisor(Employee supervisor) {this.supervisor = supervisor;}public List<string> getPhoneNumbers() {return phoneNumbers;}public void setPhoneNumbers(List<string> phoneNumbers) {this.phoneNumbers = phoneNumbers;}public void readExternal(ObjectInput objectInput) throws IOException,ClassNotFoundException {this.firstName = objectInput.readUTF();this.lastName = objectInput.readUTF();this.socialSecurityNumber = objectInput.readUTF();this.department = objectInput.readUTF();this.position = objectInput.readUTF();this.hireDate = new Date(objectInput.readLong());this.salary = objectInput.readDouble();int attributeCount = objectInput.read();byte[] attributes = new byte[attributeCount];objectInput.readFully(attributes);for (int i = 0; i < attributeCount; i++) {byte attribute = attributes[i];switch (attribute) {case (byte) 0:this.supervisor = (Employee) objectInput.readObject();break;case (byte) 1:this.phoneNumbers = Arrays.asList(objectInput.readUTF().split(";"));break;}}}public void writeExternal(ObjectOutput objectOutput) throws IOException {objectOutput.writeUTF(firstName);objectOutput.writeUTF(lastName);objectOutput.writeUTF(socialSecurityNumber);objectOutput.writeUTF(department);objectOutput.writeUTF(position);objectOutput.writeLong(hireDate.getTime());objectOutput.writeDouble(salary);byte[] attributeFlags = new byte[2];int attributeCount = 0;if (supervisor != null) {attributeFlags[0] = (byte) 1;attributeCount++;}if (phoneNumbers != null && !phoneNumbers.isEmpty()) {attributeFlags[1] = (byte) 1;attributeCount++;}objectOutput.write(attributeCount);byte[] attributes = new byte[attributeCount];int j = attributeCount;for (int i = 0; i < 2; i++)if (attributeFlags[i] == (byte) 1) {j--;attributes[j] = (byte) i;}objectOutput.write(attributes);for (int i = 0; i < attributeCount; i++) {byte attribute = attributes[i];switch (attribute) {case (byte) 0:objectOutput.writeObject(supervisor);break;case (byte) 1:StringBuilder rowPhoneNumbers = new StringBuilder();for(int k = 0; k < phoneNumbers.size(); k++)rowPhoneNumbers.append(phoneNumbers.get(k) + ";");rowPhoneNumbers.deleteCharAt(rowPhoneNumbers.lastIndexOf(";"));objectOutput.writeUTF(rowPhoneNumbers.toString());break;}}}
}

這里要注意的事情:

  • 我們實現了“ writeExternal”方法來編組“ Employee”對象。 所有必填字段都寫入流
  • 對于“ hireDate”字段,我們僅寫入此Date對象表示的毫秒數。 假設demarshaller將使用與marshaller相同的時區,毫秒值就是我們正確反序列化“ hireDate”字段所需的所有信息。 請記住,我們可以使用“ objectOutput.writeObject(hireDate)”操作來序列化整個“ hireDate”對象。 在這種情況下,默認的序列化機制將導致結果流的速度下降和大小增加
  • 所有非強制性字段(“ supervisor”和“ phoneNumbers”)只有在它們具有實際(非空)值時才被寫入流中。 為了實現此功能,我們使用“ attributeFlags”和“ attributes”字節數組。 “ attributeFlags”數組的每個位置代表一個非強制性字段,并保留一個“標記”,指示特定字段是否具有值。 我們檢查每個非必填字段,并使用相應的標記填充“ attributeFlags”字節數組。 “屬性”字節數組指示必須通過“位置”寫入流中的實際非必需字段。 例如,如果“ supervisor”和“ phoneNumbers”非必填字段均具有實際值,則“ attributeFlags”字節數組應為[1,1],而“ attributes”字節數組應為[0,1]。 如果僅“ phoneNumbers”非強制字段具有非空值,則“ attributeFlags”字節數組應為[0,1],而“ attributes”字節數組應為[1]。 通過使用上述算法,我們可以為結果流實現最小的尺寸占用。 為了正確地反序列化“ Employee”對象的非必需參數,我們必須僅將以下信息寫入流:
    • 將要寫入的非強制參數的總數(又稱“屬性”字節數組大小–供編組者解析)
    • “屬性”字節數組(供編組員正確分配字段值)
    • 實際非強制性參數值
  • 對于“ phoneNumbers”字段,我們構造并將其內容的String表示形式寫入流中。 或者,我們可以使用“ objectOutput.writeObject(phoneNumbers)”操作序列化整個“ phoneNumbers”對象。 在這種情況下,默認的序列化機制將導致結果流的速度下降和大小增加
  • 我們實現了“ readExternal”方法來對“ Employee”對象進行編組。 所有必填字段都將寫入流中。 對于非必填字段,demarshaller根據上述協議分配適當的字段值

對于序列化和反序列化過程,我們使用了以下四個功能。 這些功能有兩種形式。 第一對適用于序列化和反序列化Externalizable對象實例,而第二對適用于序列化和反序列化Serializable對象實例。

public static byte[][] serializeObject(Externalizable object) throws Exception {ByteArrayOutputStream baos = null;ObjectOutputStream oos = null;byte[][] res = new byte[2][];try {baos = new ByteArrayOutputStream();oos = new ObjectOutputStream(baos);object.writeExternal(oos);oos.flush();res[0] = object.getClass().getName().getBytes();res[1] = baos.toByteArray();} catch (Exception ex) {throw ex;} finally {try {if(oos != null)oos.close();} catch (Exception e) {e.printStackTrace();}}return res;}
public static Externalizable deserializeObject(byte[][] rowObject) throws Exception {ObjectInputStream ois = null;String objectClassName = null;Externalizable res = null;try {objectClassName = new String(rowObject[0]);byte[] objectBytes = rowObject[1];ois = new ObjectInputStream(new ByteArrayInputStream(objectBytes));Class objectClass = Class.forName(objectClassName);res = (Externalizable) objectClass.newInstance();res.readExternal(ois);} catch (Exception ex) {throw ex;} finally {try {if(ois != null)ois.close();} catch (Exception e) {e.printStackTrace();}}return res;}
public static byte[] serializeObject(Serializable object) throws Exception {ByteArrayOutputStream baos = null;ObjectOutputStream oos = null;byte[] res = null;try {baos = new ByteArrayOutputStream();oos = new ObjectOutputStream(baos);oos.writeObject(object);oos.flush();res = baos.toByteArray();} catch (Exception ex) {throw ex;} finally {try {if(oos != null)oos.close();} catch (Exception e) {e.printStackTrace();}}return res;}
public static Serializable deserializeObject(byte[] rowObject) throws Exception {ObjectInputStream ois = null;Serializable res = null;try {ois = new ObjectInputStream(new ByteArrayInputStream(rowObject));res = (Serializable) ois.readObject();} catch (Exception ex) {throw ex;} finally {try {if(ois != null)ois.close();} catch (Exception e) {e.printStackTrace();}}return res;}

下面我們展示了上述兩種方法之間的性能比較表

橫軸表示測試運行的次數,縱軸表示每次測試運行的每秒平均事務數(TPS)。 因此,較高的值更好。 如您所見,與普通的Serializable方法相比,使用Externalizable方法可以在序列化和反序列化時獲得出色的性能提升。

最后,我們必須指出我們執行了測試,為“ Employee”對象的所有非必填字段提供了值。 如果在相同方法之間進行比較時不使用所有非強制性參數,并且最重要的是在Externalizable和Serializable方法之間進行交叉比較時,您應該期望獲得更高的性能提升。

編碼愉快!

賈斯汀

相關文章 :
  • Java最佳實踐–多線程環境中的DateFormat
  • Java最佳實踐– Vector vs ArrayList vs HashSet
  • Java最佳實踐–字符串性能和精確字符串匹配
  • Java最佳實踐–隊列之戰和鏈接的ConcurrentHashMap
  • Java最佳實踐– Char到Byte和Byte到Char的轉換

翻譯自: https://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374820.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374820.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374820.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

7-18 二分法求多項式單根 (20 分)

二分法求函數根的原理為&#xff1a;如果連續函數f(x)在區間[a,b]的兩個端點取值異號&#xff0c;即f(a)f(b)<0&#xff0c;則它在這個區間內至少存在1個根r&#xff0c;即f0。 二分法的步驟為&#xff1a; 檢查區間長度&#xff0c;如果小于給定閾值&#xff0c;則停止&a…

java只使用try和finally不使用catch的原因和場景

JDK并發工具包中&#xff0c;很多異常處理都使用了如下的結構&#xff0c;如AbstractExecutorService&#xff0c;即只有try和finally沒有catch。 class X {private final ReentrantLock lock new ReentrantLock();// ...public void m(){lock.lock(); // block until condi…

Java 7:嘗試資源

本文研究try-with-resources語句的用法。 這是一個聲明一個或多個資源的try語句。 資源是一個對象&#xff0c;程序完成后必須將其關閉。 try-with-resources語句可確保在語句末尾關閉每個資源。 任何實現java.lang.AutoCloseable或java.io.Closeable接口的對象都可以用作資源。…

Spring學習(19)--- Schema-based AOP(基于配置的AOP實現) --- 配置切面aspect

Spring所有的切面和通知器都必須放在一個<aop:config>內&#xff08;可以配置包含多個<aop:config>元素&#xff09;&#xff0c;每個<aop:config>包含pointcut&#xff0c;advisor和apsect元素。ps&#xff1a;他們必須按照這個順序進行聲明 <aop:pointc…

2021-10-08

word文檔&#xff1a;.doc .docx 需求文檔、架構文檔、接口文檔、詳設文檔一般都是用word編寫。 Excel表格&#xff1a;.xls、.xlsx’&#xff0c;.csv 測試用例 PPT幻燈片&#xff1a;.ppt、*.pptx 版本不同 可執行文件&#xff08;windows系統&#xff09;&#xff1a; *.exe…

UITableViewCell 選中的狀態小技巧

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {[super setSelected:selected animated:animated]; //cell 沒被選中時 隱藏這個 _leftImageViewself.leftImageView.hidden !selected; //選中text變紅 不然變灰色self.textLabel.textColor selected ? [UICol…

Spring和AspectJ的領域驅動設計

在JavaCodeGeeks主持的上一篇文章中&#xff0c;我們的JCG合作伙伴 Tomasz Nurkiewicz介紹了使用State設計模式進行領域驅動設計的介紹 。 在該教程的最后&#xff0c;他承認他省略了如何將依賴項&#xff08;DAO&#xff0c;業務服務等&#xff09;注入域對象的過程。 但是&am…

BZOJ 3143 HNOI2013 游走 高斯消元 期望

這道題是我第一次使用高斯消元解決期望類的問題&#xff0c;首發A了&#xff0c;感覺爽爽的.... 不過筆者在做完后發現了一些問題&#xff0c;在原文的后面進行了說明。 中文題目&#xff0c;就不翻大意了&#xff0c;直接給原題&#xff1a; 一個無向連通圖&#xff0c;頂點從…

VS2019安全函數scanf_s問題

VS2017、VS2019等安全函數scanf_s問題&#xff1a; scanf()、gets()、fgets()、strcpy()、strcat() 等都是C語言自帶的函數&#xff0c;它們都是標準函數&#xff0c;但是它們都有一個缺陷&#xff0c;就是不安全&#xff0c;可能會導致數組溢出或者緩沖區溢出&#xff0c;讓黑…

eclipse啟動tomcat, http://localhost:8080無法訪問的解決方案

問題:&#xff1a; tomcat在eclipse里面能正常啟動&#xff0c;但在瀏覽器中訪問http://localhost:8080/不能訪問tomcat管理頁面&#xff0c;且報404錯誤。同時其他項目頁面也不能訪問。訪問的時候出現下列頁面: 現在關閉eclipse里面的tomcat&#xff0c;在tomcat安裝目錄下雙擊…

GWT EJB3 Maven JBoss 5.1集成教程

大家好&#xff0c; 在本文中&#xff0c;我們將演示如何正確集成GWT和EJB3 &#xff0c;以實現示例項目&#xff0c;使用maven進行構建并將其部署在JBoss 5.1應用服務器上。 實際上&#xff0c;您可以輕松地更改maven構建文件中的依賴關系&#xff0c;并將項目部署到您喜歡的…

VS2019注釋整段代碼

VS2019注釋整段代碼 1.注釋 先CTRLK&#xff0c;然后CTRLC 2.取消注釋&#xff1a; 先CTRLK&#xff0c;然后CTRLU 順便寫一下&#xff1a; VS code注釋整段代碼 Alt Shift A 注釋 CodeBlocks&#xff1a; CtrlShiftC注釋掉當前行或選中部分&#xff0c; CtrlShiftX解除注釋…

linux中的開機和關機命令

與關機、重新啟動相關的命令 * 將數據同步寫入硬盤中的命令 sync * 慣用的關機命令 shutdown * 重新啟動、關機 reboot halt poweroff sync 強制將內存中的數據寫入到硬盤當中。因為linux系統中&#xff0c;數據會緩存在內存當中&#xff0c;所以為了保證數據完整保存在硬盤…

如何在不到1ms的延遲內完成100K TPS

馬丁湯普森&#xff08;Martin Thompson&#xff09;和邁克爾巴克&#xff08;Michael Barker&#xff09;討論了通過采用一種新的基礎架構和軟件方法來構建HPC金融系統&#xff0c;以不到1ms的延遲處理超過100K TPS的問題。 一些技巧包括&#xff1a; 了解平臺 建模領域 明…

獲取時間C語言-按秒數

兩部分&#xff1a; 1.獲取秒數 2.獲取“年-月-日-時-分-秒” 1.獲取秒數 #include<time.h>//包含的頭文件int GetTime() {time_t t;t time(NULL);//另一種寫法是//time(t);//當time&#xff08;&#xff09;內參數為空時結果直接輸出&#xff0c;否則就會存儲在參數…

Spring的69個知識點

目錄 Spring 概述依賴注入Spring beansSpring注解Spring數據訪問Spring面向切面編程&#xff08;AOP&#xff09;Spring MVCSpring 概述 1. 什么是spring? Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用&#xff0c;但是有些擴展是針對構建J2EE平臺的…

python 編碼問題之終極解決

結合之前遇到的坑以及下面貼的這篇文章&#xff0c; 總結幾種python亂碼解決方案&#xff0c;如果遇到亂碼&#xff0c;不妨嘗試一下&#xff1f; 1&#xff0c;必備 #encodingutf-8 2, python編程環境編碼 import sys reload(sys) sys.setdefaultencoding(utf8) 3,不知道神馬…

GWT 2 Spring 3 JPA 2 Hibernate 3.5教程

本分步指南將介紹如何使用開發一個簡單的Web應用程序 Google的網絡工具包 &#xff08;GWT&#xff09;用于富客戶端&#xff0c;而Spring作為后端服務器端框架。 該示例Web應用程序將提供對數據庫執行CRUD&#xff08;創建檢索更新刪除&#xff09;操作的功能。 對于數據訪問層…

洛谷P1014 [NOIP1999 普及組] Cantor 表

現代數學的著名證明之一是 Georg Cantor 證明了有理數是可枚舉的。他是用下面這一張表來證明這一命題的&#xff1a; 代碼 import java.util.*; public class Main{public static void main(String[] args){//int x1 0;int i 0;Scanner sc new Scanner(System.in);int n s…

3522: [Poi2014]Hotel( 樹形dp )

枚舉中點x( 即選出的三個點 a , b , c 滿足 dist( x , a ) dist( x , b ) dist( x , c ) ) , 然后以 x 為 root 做 dfs , 顯然兩個位于 x 的同一顆子樹內的點是不可能被同時選到的 . 我們對 x 的每一顆子樹進行 dfs , 記錄下當前子樹中的點到 x 距離為 d ( 1 < d < n )…