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.mzph.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 )…