背景描述:
项目是普通的java服务,以jar包的形式部署,前几天升级了一下服务,部署运行后跟踪日志发现时不时抛异常信息。
具体报错信息如下:
[2013-09-22 17:30:53,653] - [pool-3-thread-3] - ERROR - Caught IOException decoding 1061 bytes of data java.io.InvalidClassException: com.chenzhou.test.model.VmCategoryMapper; local class incompatible: stream classdesc serialVersionUID = 1411005385568342784, local class serialVersionUID = -2356515191808014047
根据报错信息来看,是在反序列化VmCategoryMapper对象时发现serialVersionUID不一致,所以抛了IO异常。
我查看了一下VmCategoryMapper类的代码,具体如下(属性和方法就省略了):
package com.chenzhou.test.model; import java.io.Serializable; import java.util.List; public class VmCategoryMapper implements Serializable { /** * serialVersionUID:序列化ID. */ private static final long serialVersionUID = -2356515191808014047L; //省略了属性和方法 }
很纳闷的是,Eclipse给我自动生成的serialVersionUID就是-2356515191808014047,那异常信息声明中的反序列化得到的1411005385568342784是怎么得来的呢。
我通过SVN历史查看了一下VmCategoryMapper类的版本历史记录,发现历史版本中也没有使用过1411005385568342784作为serialVersionUID。
不过还是发现了一个线索,就是升级之前,VmCategoryMapper类我并没有显示地定义serialVersionUID,这次在优化代码的时候使用IDE自动生成了一个非默认的序列化ID。熟悉java序列化的人应该都了解,如果类实现了Serializable但是没有显式声明serialVersionUID,那么在程序编译时会自己生成这个版本序列化ID,所以我猜测1411005385568342784应该是我服务升级前默认生成的序列化ID。
但是问题依然存在,我升级后明明已经显示声明了serialVersionUID的值,为什么反序列化时会得到原来默认的值呢。我把线上的jar包中的VmCategoryMapper类的class文件进行了反编译,发现反编译出来的serialVersionUID是我代码中显示声明的值,也就是-2356515191808014047。说明jar包是正常的。
百思不得其解后,同事提醒我,什么时候会进行序列化和反序列化?
经他一提醒,我突然想到了对象在缓存中读取和更新时会进行序列化反序列化,很可能是缓存惹的祸,于是赶紧检查了一下缓存逻辑所在的DAO层的方法:
@ReadThroughSingleCache(namespace = CacheConst.VM_CATEGORY, expiration = 3600) public List<VmCategoryMapper> getVMCategorys(@ParameterValueKeyProvider String code) { //get VmCategoryMapper from db }Dao中对VmCategoryMapper的读取方法见上面的代码,我们项目中使用了simple-spring-memcached来进行缓存管理。expiration = 3600代表超时时间为1个小时。因为我在服务升级时并没有清理缓存,所以memcached中存在有未超时的缓存对象。这些缓存对象是在升级前存入缓存的,序列化时使用的是原来的默认serialVersionUID也就是1411005385568342784,当服务升级后,由于新的VmCategoryMapper中显示声明了serialVersionUID的值为-235651519180801404,所以如果当查询VmCategoryMapper时,缓存命中,取出memcached中的对象进行反序列化,就会导致serialVersionUID不一致。
相关推荐
Java序列化是Java平台中的一种标准机制,允许将对象的状态转换为...通过以上知识点的学习,你可以对Java序列化有深入的理解,无论是理论还是实践,都能为你在开发过程中处理对象持久化、数据传输等问题提供有力支持。
总结,Java序列化是一个强大的工具,但使用时需注意其潜在的风险和性能影响。理解序列化的工作原理,并合理运用,可以在许多场景下提高软件的灵活性和可维护性。在实际项目中,结合淘宝等大型系统的具体需求,序列化...
1. **序列化标识符(SerialVersionUID)**:Java允许你为每个可序列化的类定义一个唯一的`serialVersionUID`,默认是由JVM根据类的结构计算出来的。如果类的版本更新导致结构变化,而此值未做更新,反序列化时会抛出`...
2. ** serialVersionUID**:为了保证序列化版本的兼容性,每个可序列化类应定义一个`serialVersionUID`字段。如果不显式定义,Java会自动生成一个,但当类结构改变时可能导致异常。 3. **ObjectInputValidation**:...
3. `java.io.NotSerializableException`: 当尝试序列化一个未实现Serializable接口的对象时抛出的异常。 4. `sun.misc.BASE64Encoder`和`sun.misc.BASE64Decoder`(非标准API):用于将字节数组编码为Base64字符串,...
- 在Java中,如果一个类想要实现序列化,只需实现`Serializable`接口即可,但该接口没有任何方法,只是一个标志接口。 3. **序列化过程**: - 使用`ObjectOutputStream`类的`writeObject()`方法将对象序列化到...
2. **serialVersionUID**: 每个可序列化的类都有一个`serialVersionUID`,用于检查序列化版本的一致性。如果类的版本发生变化但此ID未更新,反序列化时可能会抛出`InvalidClassException`。 3. **writeObject()和...
在Java编程中,文件序列化是一个常见的操作,用于将对象的状态转换为字节流,以便存储或在网络上传输。然而,序列化过程可能会消耗大量时间,尤其是在处理大量数据时。以下是一些加快Java文件序列化速度的方法: 1....
通过实现`java.io.Serializable`接口,一个类的实例就能被序列化。 2. **默认序列化过程** 默认情况下,序列化会保存对象的所有实例变量,但不包括静态变量。这是因为静态变量属于类,而非特定的对象实例。序列化...
在Java中,要使一个类能够被序列化,我们需要让该类实现`java.io.Serializable`接口。例如: ```java public class MyObject implements Serializable { private String name; private int age; // 构造器,...
### Java序列化与反序列化的深入解析 #### Java序列化的重要性及应用场景 Java序列化是一项核心功能,它允许程序员将对象的状态转化为字节流的形式,从而实现对象的持久化存储或者在网络之间进行传递。这对于诸如...
这篇博客讨论的就是这类问题,特别是如何理解和解决由序列化工具导致的困扰。 在Redis中,当我们存储复杂的数据结构,如Java对象时,需要将对象转换为可存储的格式,这就是序列化的过程。默认情况下,Redis并不提供...
Netty提供了一个`SerializationCodec`类,它是`ChannelInboundHandler`和`ChannelOutboundHandler`的实现,专门用于处理Java序列化的编码和解码。在编码过程中,`SerializationCodec`会调用`ObjectOutputStream`将...
在实际应用中,对象序列化有时会导致安全问题,如序列化攻击。因此,应谨慎处理反序列化的数据,避免来自不可信来源的字节流直接反序列化为对象。此外,序列化可能会导致内存泄漏,尤其是在使用缓存或持久化数据时,...
Java序列化机制是Java平台提供的一种标准方法,用于将对象的状态转换为字节序列,以便在网络中传输或存储在磁盘、数据库等持久化介质中。这一机制在分布式环境、远程通信、持久化存储和数据交换等多个场景下具有广泛...
Java序列化是将对象的状态转换为字节流的过程,以便可以存储这些字节流或在网络上传输。在“利用java序列化实现基于文件的快速索引”这一主题中,我们将探讨如何利用Java的序列化机制来创建高效的数据索引,从而加速...
Java序列化和反序列化是Java开发中常见且重要的概念,尤其在面试中常常被问及。序列化是指将一个Java对象转化为字节序列的过程,这样可以将对象的状态持久化到磁盘上或者在网络中进行传输。反序列化则是相反的过程,...
Java序列化是指将一个Java对象的状态转换为字节流,以便于存储或通过网络进行传输的过程。当接收端接收到这些字节流后,可以将其重新转换为原始的对象状态。这个过程对于分布式系统的数据交换非常重要。 #### 二、...
缓存主要可分为二大类:一、通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以 XML 格式、序列化文件 DAT 格式还是其它文件格式;二、内存缓存,也就是实现一个类中静态 Map,对这个 Map 进行常规...
除了标准的Java序列化,还有其他序列化框架,如`protobuf`、`JSON`、`XML`等,它们提供了更高效、更灵活的序列化方案。例如,`protobuf`是Google开发的一种紧凑、快速、跨平台的数据交换格式,适用于网络通信和数据...