2016-02-12 89 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()来保留该代码。

相关问题