2016-02-12 93 views
2

我想在Java 7中使用ObjectOutputStream編寫文件的第三方Externalizable類實例(Drools KnowledgePackage)的集合(ArrayList)。如果我限制KnowledgePackage的大小,以便生成的文件爲< = 1GB一切正常。如果我讓實例進一步增長一點,以至於(我相信)文件大於1GB,那麼我會在下面得到失敗。ObjectOutputStream - 超過1GB的對象導致java.lang.OutOfMemoryError:請求的數組大小超過VM限制

的代碼看起來是這樣的:

Collection<KnowledgePackage> kpkgs = kbuilder.getKnowledgePackages(); 

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(packageFileName)); 
out.writeObject(kpkgs); 
out.close(); 

和錯誤看起來是這樣的:

Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit 
     at java.util.Arrays.copyOf(Arrays.java:2271) 
     at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:113) 
     at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) 
     at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:140) 
     at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1876) 
     at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1785) 
     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1188) 
     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) 
     at org.drools.rule.Package.writeExternal(Package.java:164) 
     at org.drools.definitions.impl.KnowledgePackageImp.writeExternal(KnowledgePackageImp.java:161) 
     at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1458) 
     at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1429) 
     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177) 
     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) 
     at java.util.ArrayList.writeObject(ArrayList.java:742) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
     at java.lang.reflect.Method.invoke(Method.java:606) 
     at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) 
     at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1495) 
     at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431) 
     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177) 
     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) 
     ... <my code> 

增加堆大小沒有任何區別。如果在這裏繼續,這似乎是別的。

我相信原因是ObjectOutputStream的內部字節數組管理。根據https://bugs.openjdk.java.net/browse/JDK-6991552https://bugs.openjdk.java.net/browse/JDK-6464834,陣列大小每增加一個現有陣列就會增加2 + 1。這意味着當數組達到> = 1GB時,如果不刪除Java的(2^31)-1最大數組大小,就無法進一步增長。

是否有解決方法或替代方法來編寫這些對象,所以我至少可以輸出2GB,理想情況下是無限大小。也許有另一種編寫和閱讀這種大型對象的方法存在?

已經嘗試了HotSpot 1.7.0_51和OpenJDK 1.7.0_45,結果相同。 在情況下,其相關的,Drools的版本是5.5.0Final

非常感謝

+1

您是否嘗試寫入新的DroolsObjectOutputStream(新的FileOutputStream(packageFileName)); ? – laune

+0

@laune,謝謝。這正是我所做的,並且由於下面的@jtahlborn所解釋的原因,它現在可以工作。毫不奇怪,我不得不改變後面讀取包在'ObjectInputStream in = new DroolsObjectInputStream(new FileInputStream(packageFileName))';' –

回答

4

問題不在於jdk,而在於Drools。如果您查看堆棧跟蹤,則問題是該對象正被序列化爲ByteArrayOutputStream。 jdk沒有這樣做,那就是如何實現org.drools.rule.Package.writeExternal方法:http://grepcode.com/file/repo1.maven.org/maven2/org.drools/drools-core/5.4.0.Final/org/drools/rule/Package.java#Package.writeExternal%28java.io.ObjectOutput%29

你應該用drools(關於序列化更大的規則包)提交一個bug。

或者,您看起來好像使用DroolsObjectOutputStream,然後它跳過次內存序列化並直接使用給定的流。這可能會解決您的問題(假設您可以將DOOS用於您的用例)。

+1

Spot on的代碼。謝謝。只檢查堆棧跟蹤我沒有意識到這是在org.drools.rule.Package.writeExternal之上和之下引用的ObjectOutputStream的不同實例。當我查看鏈接到的org.drools.rule.Package.writeExternal代碼時,立即顯而易見。這會教會我在開源和可用時不要深入第三方代碼。 –

0

我通常使用另一種序列化機制爲大型,複雜的對象,如JSON或XML。但是,不要重寫代碼以使用JSON和XML,您可以通過將大類的簽名更改爲java.io.Exernalizable並實施和writeExternal()來保留該代碼。

相關問題