处理序列化缓存问题

问题描述

如果你的硬盘不够大也不用担心,Java完全可以自己解决硬盘容量问题。这个例子对你的电脑唯一的要求就是必须有256M以上的内存,并且要设置执行参数为-Xmx256m。相信现在没有谁的电脑内存是不够256M的。importjava.io.*;publicclassSuperFastWriter{privatestaticfinallongTERA_BYTE=1024L*1024*1024*1024;publicstaticvoidmain(String[]args)throwsIOException{longbytesWritten=0;byte[]data=newbyte[100*1024*1024];ObjectOutputStreamout=newObjectOutputStream(newBufferedOutputStream(newFileOutputStream("bigdata.bin")));longtime=System.currentTimeMillis();for(inti=0;i<10*1024*1024;i++){out.writeObject(data);bytesWritten+=data.length;}out.writeObject(null);out.close();time=System.currentTimeMillis()-time;System.out.printf("Wrote%dTB%n",bytesWritten/TERA_BYTE);System.out.println("time="+time);}}编译之后,我们就可以执行这个程序了。java-Xmx256mSuperFastWriter可以看到类似以下的输出Wrote1000TBtime=3710你一定会非常奇怪,我用的到底是什么电脑。不仅输出的速度那么快,并且输出的内容完全超出了硬盘容量。每秒钟250TB,简直是不可思议的事情。如果到硬盘上看一下输出的文件,会发现文件只有大概150M。这是因为当我们通过ObjectOutputStream输出一个对象的时候,ObjectOutputStream会将该对象保存到一个哈希表中,以后在输出相同的对象,都会只输出指针,不输出内容。同样的事情也发生在读取对象的时候。Java通过该机制达到最小化数据输入和输出的目的。下面的例子就演示了读取的过程。importjava.io.*;publicclassSuperFastReader{privatestaticfinallongTERA_BYTE=1024L*1024*1024*1024;publicstaticvoidmain(String[]args)throwsException{longbytesRead=0;ObjectInputStreamin=newObjectInputStream(newBufferedInputStream(newFileInputStream("bigdata.bin")));longtime=System.currentTimeMillis();byte[]data;while((data=(byte[])in.readObject())!=null){bytesRead+=data.length;}in.close();time=System.currentTimeMillis()-time;System.out.printf("Read%dTB%n",bytesRead/TERA_BYTE);System.out.println("time="+time);}}在这个例子中,我们去读取刚才输出的文件。虽然文件只有150M左右,但是实际读取的时候,数据量应该是和写出的一样。程序执行时间只需要几秒时间。类似执行结果是:Read1000TBtime=2033前面的例子我们反复的将同一个数组写出到文件中,但是并没有修改数组的内容。下面的例子我们将每次写出内容不同的数组。因为Arrays.fill()的执行效率比较低。所以我们只写出256个大数组。importjava.io.*;importjava.util.Arrays;publicclassModifiedObjectWriter{publicstaticvoidmain(String[]args)throwsIOException{byte[]data=newbyte[10*1024*1024];ObjectOutputStreamout=newObjectOutputStream(newBufferedOutputStream(newFileOutputStream("smalldata.bin")));for(inti=-128;i<128;i++){Arrays.fill(data,(byte)i);out.writeObject(data);}out.writeObject(null);out.close();}}接下来,我们把写出的内容在从文件中读出看看。importjava.io.*;publicclassModifiedObjectReader{publicstaticvoidmain(String[]args)throwsException{ObjectInputStreamin=newObjectInputStream(newBufferedInputStream(newFileInputStream("smalldata.bin")));byte[]data;while((data=(byte[])in.readObject())!=null){System.out.println(data[0]);}in.close();}}观察会发现,读出的内容并没有-128,-127,-126等数字,只有-128。这是因为虽然每次我们写出之前都修改了数据的内容,但是依然是原来的数组。Java序列化机制除了第一次写出数组内容以外,以后每次只写出一个指针。在读的时候,也就只第一次读取到内容为-128的数组,以后每次都根据读取到的指针反复在本地哈希表中读取了。也就是说序列化机制只关心对象是否变化,而不关心内容是否变化。通过这些提点,我们可以看出序列化的原则是:如果需要重复序列化一个对象,并且两次序列化之间对象的内容会发生改变,那么就要复位输出流。或者每次输出前都重新创建一个对象。下面我们看一下每次都创建新对象的结果:publicclassModifiedObjectWriter2{publicstaticvoidmain(String[]args)throwsIOException{ObjectOutputStreamout=newObjectOutputStream(newBufferedOutputStream(newFileOutputStream("verylargedata.bin")));for(inti=-128;i<128;i++){byte[]data=newbyte[10*1024*1024];Arrays.fill(data,(byte)i);out.writeObject(data);}out.writeObject(null);out.close();}}当程序运行一会之后,将会提示OutOfMemoryError。这是因为每次对象写出的时候,都会在哈希表中保留一个指针,所以虽然对象已经不再使用了,Java的垃圾回收机制也不会对对象进行回收,要一直等到输出流复位为止。当循环多次执行的时候,创建的对象越来越多,并且没有被及时回收,就会出现OutOfMemoryError问题了。通过观察可以发现,在出现错误之前所产生的文件基本接近于为JVM所分配的内存大小。如果每次输出之后,都复位输出,就可以避免这个问题了。importjava.io.*;importjava.util.Arrays;publicclassModifiedObjectWriter3{publicstaticvoidmain(String[]args)throwsIOException{ObjectOutputStreamout=newObjectOutputStream(newBufferedOutputStream(newFileOutputStream("verylargedata.bin")));byte[]data=newbyte[10*1024*1024];for(inti=-128;i<128;i++){Arrays.fill(data,(byte)i);out.writeObject(data);out.reset();}out.writeObject(null);out.close();}}不幸的是,复位输出为导致所有的对象都被清理,即使是需要重复输出的对象。对ObjectOutputStream和ObjectInputStream进行优化设计很大程度上降低了重复数据的输入输出工作,比如字符串。不幸的是,如果不恰当的使用会经常导致OutOfMemoryError错误或者输出数据不完整。

时间: 2024-10-28 06:46:52

处理序列化缓存问题的相关文章

PHP文件缓存的性能测试

前言: 在开发MooPHP的过程中,为了寻找更为高效的缓存方式,对两种最常用的缓存方式进行了测试. PHP常用缓存方式: 第一种,把需要缓存的数据进行处理,形成PHP可以直接执行的文件.在需要缓存数据的时候,通过include方式引入,并使用. 第二种,把需要的数据通过serialize函数序列化后直接保存到文件.在需要使用缓存数据的时候,通过反序列化读入文件内容并复制给需要的变量,然后使用. 测试结果: 通过测试我们发现,第二种也就是serialize缓存数据的方式更加高效.(数据略去,最后提

PHP 文件缓存的性能测试_php技巧

PHP常用缓存方式:第一种,把需要缓存的数据进行处理,形成PHP可以直接执行的文件.在需要缓存数据的时候,通过include方式引入,并使用.第二种,把需要的数据通过serialize函数序列化后直接保存到文件.在需要使用缓存数据的时候,通过反序列化读入文件内容并复制给需要的变量,然后使用. 测试结果:通过测试我们发现,第二种也就是serialize缓存数据的方式更加高效.(数据略去,最后提供了文章地址下载,大家可以自行测试) 原因分析:include方式读取缓存的时候,PHP需要执行几个过程1

请教C#下如何序列化自定义结构或类?

问题描述 各位高手:请问在C#中使用Binaryformater将自定义数据结构序列化时,如定义结构structSTData{intnum;chartype;shortarr;doublex;};结构采用单字节对齐方式,序列化后字节的数组比结构实际的字节数要大几倍,如何让序列化后字节数组的大小等于实际结构的字节数?由于需要C#通过UDP与VC6之间进行数据通信,在VC6中结构使用单字节对齐后,转换后的字节数组就是实际结构的字节之和,而如果C#转换后的字节数组大小与VC6的字节数组大小不同就无法确

PHP常用缓存方式:

PHP常用缓存方式: 第一种,把需要缓存的数据进行处理,形成PHP可以直接执行的文件.在需要缓存数据的时候,通过include方式引入,并使用. 第二种,把需要的数据通过serialize函数序列化后直接保存到文件.在需要使用缓存数据的时候,通过反序列化读入文件内容并复制给需要的变量,然后使用. 测试结果: 通过测试我们发现,第二种也就是serialize缓存数据的方式更加高效.(数据略去,最后提供了文章地址下载,大家可以自行测试) 原因分析: include方式读取缓存的时候,PHP需要执行几

PHP常用缓存方式入门教程

PHP常用缓存方式入门教程 第一种,把需要缓存的数据进行处理,形成PHP可以直接执行的文件.在需要缓存数据 的时候,通过include方式引入,并使用. 第二种,把需要的数据通过serialize函数序列化后直接保存到文件.在需要使用缓存 数据的时候,通过反序列化读入文件内容并复制给需要的变量,然后使用. 测试结果: 通过测试我们发现,第二种也就是serialize缓存数据的方式更加高效.(数据略去, 最后提供了文章地址下载,大家可以自行测试) 原因分析: include方式读取缓存的时候,PH

Spark配置参数

以下是整理的Spark中的一些配置参数,官方文档请参考Spark Configuration. Spark提供三个位置用来配置系统: Spark属性:控制大部分的应用程序参数,可以用SparkConf对象或者Java系统属性设置 环境变量:可以通过每个节点的 conf/spark-env.sh脚本设置.例如IP地址.端口等信息 日志配置:可以通过log4j.properties配置 Spark属性 Spark属性控制大部分的应用程序设置,并且为每个应用程序分别配置它.这些属性可以直接在Spark

transient关键字使用笔记

1.transient的作用及使用方法 一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 在实际开发过程中,有时候会遇到这样的情况: 一个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓

MyBatis底层基础和拦截器 - 第一部分

MyBatis底层基础和拦截器 - 第一部分 第一部分包含了下面代码的基本讲解和下面代码与XML配置关系和作用的讲解. 这一部分是了解后续拦截器和SqlSource的重要基础. 本视频不仅对深入学习MyBatis有用,对于一般的MyBatis使用也能加深理解. 第一部分完整视频+源文件+PPT下载地址:http://pan.baidu.com/s/1mgzZnx2 在第一部分视频后,还有一些和MyBatis相关问答的一些视频,这些视频可以从群共享下载. 另外想看第三遍讲解的各位还有看直播的机会,

Spark调优经验总结

概述 本文以Spark实践经验和Spark原理为依据,总结了Spark性能调优的一些方法.这些总结基于Spark-1.0.0版本.对于最近推出的Spark-1.1.0版本,本文介绍了几个版本增强. Spark性能调优 Executor和分区 Executor是一个独立的JVM进程,每个任务会有独立的线程来执行,Executor最大可并发任务数量与其拥有的核心数量相同,执行过程中的数据缓存放在Executor的全局空间中.根据以上我们可以得出: 同一个Executor中执行的任务,可以共享同一个数