成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

有趣的Java對象序列化緩存問題

開發 后端
Java的序列化機制對相同的對象進行了優化。向流中輸出不同對象的時候,如果沒有進行復位,將導致內存泄露。對同一個對象,如果輸出多次只會有一次被保留。如果復位,則失去了優化所帶來的好處。

【51CTO特稿】在這里我們將通過幾個有趣的例子,來演示Java對象序列化緩存問題。下面這個程序非常神奇,用了不到4秒的時間就向我的硬盤上輸出了1000TB的數據。不要懷疑你看錯了,確實是不到4秒時間就輸出1000TB的數據,不相信你也可以在你的電腦上運行一下這個程序。如果你的硬盤不夠大也不用擔心,Java完全可以自己解決硬盤容量問題。這個例子對你的電腦***的要求就是必須有256M以上的內存,并且要設置執行參數為-Xmx256m。相信現在沒有誰的電腦內存是不夠256M的。
 

  1. import java.io.*;  
  2.  
  3. public class SuperFastWriter {  
  4.     private static final long TERA_BYTE = 1024L * 1024 * 1024 * 1024;  
  5.     public static void main(String[] args) throws IOException {  
  6.         long bytesWritten = 0;  
  7.         byte[] data = new byte[100 * 1024 * 1024];  
  8.         ObjectOutputStream out = new ObjectOutputStream(  
  9.             new BufferedOutputStream(  
  10.                 new FileOutputStream("bigdata.bin")  
  11.             )  
  12.         );  
  13.         long time = System.currentTimeMillis();  
  14.         for (int i = 0; i < 10 * 1024 * 1024; i++) {  
  15.             out.writeObject(data);  
  16.             bytesWritten += data.length;  
  17.         }  
  18.         out.writeObject(null);  
  19.         out.close();  
  20.         time = System.currentTimeMillis() - time;  
  21.         System.out.printf("Wrote %d TB%n", bytesWritten / TERA_BYTE);  
  22.         System.out.println("time = " + time);  
  23.     }  
  24. }  

編譯之后,我們就可以執行這個程序了。

java -Xmx256m SuperFastWriter

可以看到類似以下的輸出

Wrote 1000 TB

time = 3710

你一定會非常奇怪,我用的到底是什么電腦。不僅輸出的速度那么快,并且輸出的內容完全超出了硬盤容量。每秒鐘250 TB,簡直是不可思議的事情。

如果到硬盤上看一下輸出的文件,會發現文件只有大概150M。這是因為當我們通過ObjectOutputStream輸出一個對象的時候,ObjectOutputStream會將該對象保存到一個哈希表中,以后在輸出相同的對象,都會只輸出指針,不輸出內容。同樣的事情也發生在讀取對象的時候。Java通過該機制達到最小化數據輸入和輸出的目的。下面的例子就演示了讀取的過程。

  1. import java.io.*;  
  2.  
  3. public class SuperFastReader {  
  4.     private static final long TERA_BYTE = 1024L * 1024 * 1024 * 1024;  
  5.     public static void main(String[] args) throws Exception {  
  6.         long bytesRead = 0;  
  7.         ObjectInputStream in = new ObjectInputStream(  
  8.             new BufferedInputStream(  
  9.                 new FileInputStream("bigdata.bin")  
  10.             )  
  11.         );  
  12.         long time = System.currentTimeMillis();  
  13.         byte[] data;  
  14.         while ((data = (byte[]) in.readObject()) != null) {  
  15.             bytesRead += data.length;  
  16.         }  
  17.         in.close();  
  18.         time = System.currentTimeMillis() - time;  
  19.         System.out.printf("Read %d TB%n", bytesRead / TERA_BYTE);  
  20.         System.out.println("time = " + time);  
  21.     }  
  22. }  

在這個例子中,我們去讀取剛才輸出的文件。雖然文件只有150M左右,但是實際讀取的時候,數據量應該是和寫出的一樣。程序執行時間只需要幾秒時間。類似執行結果是:

Read 1000 TB

time = 2033

前面的例子我們反復的將同一個數組寫出到文件中,但是并沒有修改數組的內容。下面的例子我們將每次寫出內容不同的數組。因為Arrays.fill()的執行效率比較低。所以我們只寫出256個大數組。
 

  1. import java.io.*;  
  2. import java.util.Arrays;  
  3.  
  4. public class ModifiedObjectWriter {  
  5.     public static void main(String[] args) throws IOException {  
  6.         byte[] data = new byte[10 * 1024 * 1024];  
  7.         ObjectOutputStream out = new ObjectOutputStream(  
  8.             new BufferedOutputStream(  
  9.                 new FileOutputStream("smalldata.bin")  
  10.             )  
  11.         );  
  12.         for (int i = -128; i < 128; i++) {  
  13.             Arrays.fill(data, (byte) i);  
  14.             out.writeObject(data);  
  15.         }  
  16.         out.writeObject(null);  
  17.         out.close();  
  18.     }  
  19. }  

 接下來,我們把寫出的內容在從文件中讀出看看。 

  1. import java.io.*;  
  2.  
  3. public class ModifiedObjectReader {  
  4.     public static void main(String[] args) throws Exception {  
  5.         ObjectInputStream in = new ObjectInputStream(  
  6.             new BufferedInputStream(  
  7.                 new FileInputStream("smalldata.bin")  
  8.             )  
  9.         );  
  10.         byte[] data;  
  11.         while ((data = (byte[]) in.readObject()) != null) {  
  12.         System.out.println(data[0]);  
  13.         }  
  14.         in.close();  
  15.     }  
  16. }  

觀察會發現,讀出的內容并沒有-128, -127, -126等數字,只有-128。這是因為雖然每次我們寫出之前都修改了數據的內容,但是依然是原來的數組。Java序列化機制除了***次寫出數組內容以外,以后每次只寫出一個指針。在讀的時候,也就只***次讀取到內容為-128的數組,以后每次都根據讀取到的指針反復在本地哈希表中讀取了。也就是說序列化機制只關心對象是否變化,而不關心內容是否變化。

通過這些提點,我們可以看出序列化的原則是:如果需要重復序列化一個對象,并且兩次序列化之間對象的內容會發生改變,那么就要復位輸出流。或者每次輸出前都重新創建一個對象。

下面我們看一下每次都創建新對象的結果:

  1. public class ModifiedObjectWriter2 {  
  2.     public static void main(String[] args) throws IOException {  
  3.         ObjectOutputStream out = new ObjectOutputStream(  
  4.             new BufferedOutputStream(  
  5.                 new FileOutputStream("verylargedata.bin")  
  6.             )  
  7.         );  
  8.         for (int i = -128; i < 128; i++) {  
  9.             byte[] data = new byte[10 * 1024 * 1024];  
  10.             Arrays.fill(data, (byte) i);  
  11.             out.writeObject(data);  
  12.         }  
  13.         out.writeObject(null);  
  14.         out.close();  
  15.     }  
  16. }  

當程序運行一會之后,將會提示OutOfMemoryError。這是因為每次對象寫出的時候,都會在哈希表中保留一個指針,所以雖然對象已經不再使用了,Java的垃圾回收機制也不會對對象進行回收,要一直等到輸出流復位為止。當循環多次執行的時候,創建的對象越來越多,并且沒有被及時回收,就會出現OutOfMemoryError問題了。通過觀察可以發現,在出現錯誤之前所產生的文件基本接近于為JVM所分配的內存大小。如果每次輸出之后,都復位輸出,就可以避免這個問題了。

 

  1. import java.io.*;  
  2. import java.util.Arrays;  
  3.  
  4. public class ModifiedObjectWriter3 {  
  5.     public static void main(String[] args) throws IOException {  
  6.         ObjectOutputStream out = new ObjectOutputStream(  
  7.             new BufferedOutputStream(  
  8.                 new FileOutputStream("verylargedata.bin")  
  9.             )  
  10.         );  
  11.         byte[] data = new byte[10 * 1024 * 1024];  
  12.         for (int i = -128; i < 128; i++) {  
  13.             Arrays.fill(data, (byte) i);  
  14.             out.writeObject(data);  
  15.             out.reset();  
  16.         }  
  17.         out.writeObject(null);  
  18.         out.close();  
  19.     }  

不幸的是,復位輸出為導致所有的對象都被清理,即使是需要重復輸出的對象。
對ObjectOutputStream和ObjectInputStream進行優化設計很大程度上降低了重復數據的輸入輸出工作,比如字符串。不幸的是,如果不恰當的使用會經常導致OutOfMemoryError錯誤或者輸出數據不完整。

【編輯推薦】

  1. Java序列化的機制和原理
  2. Java Socket通信的序列化和反序列化代碼介紹
  3. Java輸入數據流詳解
  4. Java語言深入 文件和流
  5. Java對象序列化
責任編輯:佚名 來源: 51CTO
相關推薦

2018-03-19 10:20:23

Java序列化反序列化

2009-06-14 22:01:27

Java對象序列化反序列化

2011-06-01 15:05:02

序列化反序列化

2009-03-10 13:38:01

Java序列化字節流

2012-04-13 10:45:59

XML

2009-09-09 14:45:41

XML序列化和反序列化

2009-09-09 15:47:27

XML序列化和反序列化

2012-02-14 10:29:02

Java

2019-11-20 10:07:23

web安全PHP序列化反序列化

2021-08-30 12:25:12

Python序列化函數

2022-08-06 08:41:18

序列化反序列化Hessian

2016-12-05 18:32:08

序列化androidjava

2013-03-11 13:55:03

JavaJSON

2010-03-19 15:54:21

Java Socket

2009-08-25 15:15:08

C#對象序列化應用

2024-03-05 12:49:30

序列化反序列化C#

2011-04-02 09:04:49

Java序列化

2009-08-24 17:14:08

C#序列化

2013-02-27 09:58:32

JavaJID

2013-02-28 10:00:43

JIDJava序列化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91视频91| 久久99国产精品久久99果冻传媒 | 日日摸日日碰夜夜爽亚洲精品蜜乳 | 天天看天天干 | 久久精品网 | 一区二区伦理电影 | 在线观看中文字幕av | 欧美成年人视频在线观看 | 高清一区二区三区 | 黄网站涩免费蜜桃网站 | 久久精品一区二区视频 | 免费一级欧美在线观看视频 | 精品久久久久久亚洲国产800 | 欧美日韩中文字幕在线 | 一区二区三区视频免费观看 | 国产日韩视频 | 日韩三级在线观看 | 久久99视频这里只有精品 | av资源中文在线 | 欧美性久久 | 久久久一二三 | 毛片一级片 | 午夜精品久久久久久久久久久久久 | 中文字幕一区二区三区在线视频 | 福利社午夜影院 | 99精品在线 | 九九亚洲 | 亚洲视频在线看 | 中文字幕国产 | 国产高清精品一区二区三区 | 色播久久 | 亚洲网在线 | 亚洲成年影院 | 中文字幕日韩欧美一区二区三区 | 日本 欧美 国产 | 日本久久网 | 91久久精品国产91久久性色tv | 台湾佬成人网 | 一级片av | 国内精品免费久久久久软件老师 | 一级黄色片网址 |