2010-06-26 175 views
1

该第三方脚本保存引起堆内存例外:为什么此Java/Groovy代码会导致堆内存异常?

byte[] forwardMessage(byte[] content) { 
    s = new Socket("172.17.0.30", 10001); 
    s.withStreams {InputStream input, OutputStream output -> 
     output.write content 
     return readRtsData(input) 
    } 
} 

byte[] readRtsData(input) { 
    def vplEndByte = 0xff 

    def inStream = new BufferedInputStream(input) 

    def bytes = [] 
    while (bytes.isEmpty() || bytes.last() != vplEndByte) { 
     bytes.add(inStream.read()) 
    } 

    bytes 
} 

的脚本,该脚本接收到该消息后,通过TCP/IP接收消息的那部分,会导致以下情况例外:

Exception in thread "Thread-2" org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.OutOfMemoryError: Java heap space at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:92) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:880) at groovy.lang.Closure.call(Closure.java:279) at groovy.lang.Closure.call(Closure.java:292) at org.codehaus.groovy.runtime.DefaultGroovyMethods$6.run(DefaultGroovyMethods.java:11563) at java.lang.Thread.run(Thread.java:636) Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2746) at java.util.ArrayList.ensureCapacity(ArrayList.java:187) at java.util.ArrayList.add(ArrayList.java:378) at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:616) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:229) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) at RTSGatewayServer.readRtsData(RTSGatewayServer.groovy:46) 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:616) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:361) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:880) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:151) at RTSGatewayServer$_forwardMessage_closure2.doCall(RTSGatewayServer.groovy:35) 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:616) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:880) at groovy.lang.Closure.call(Closure.java:279) at org.codehaus.groovy.runtime.DefaultGroovyMethods.withStreams(DefaultGroovyMethods.java:11462) at org.codehaus.groovy.runtime.dgm$658.invoke(Unknown Source)

我估计有更好的更有效的记忆方式,然后使用bytes.add(...)

如果任何人都可以将结果与.NET中发生的结果进行比较,那么我将更好,因为我是.NET开发人员。

+0

看到这一个:p,有点题外话但很有趣:p – TimothyP 2010-06-26 09:36:18

回答

3

因此,该脚本不断读取和存储字节,直到它看到0xff。

这似乎是一个有缺陷的设计。不管你如何调整JVM,你最终可能会用完memry。如果远程服务选择在0xff之前发送peta字节的消息,那么你将耗尽内存。我的看法是,你应该总是假定其他参与者可能会被打破或反社会。

因此,我会设置一些上限来准备接受多少数据。然后,如果可能的话,处理你已经接收到的块并返回并获取下一个块,或者如果不可能以礼貌的方式处理大块的错误消息并停止。

底线:消毒您的输入。让外部过程耗尽你的记忆是一件坏事。当他们说“不可能发生”时不要相信他们。

+0

远不只是消毒输入,循环正在寻找一个不保证发生的条件 - 即,该流将包含一个0xff AT ALL。如果流没有收到,那么循环将继续追加永久收到的最终字符。根据合同,在流关闭后,read方法将永远返回-1,因此不会让糟糕的actor将此循环发送到螺旋 - 网络hiccough会做到这一点! – 2015-06-25 01:07:44

1

用-Xmx调整jvm ...?

+0

没有帮助,但是,我也不认为这是一个好的解决方案......我从未遇到过我自己的这类问题c#code ...我想我会同意djna,thnx虽然 – TimothyP 2010-06-26 09:22:59

1

它实际上是来自Socket的导致OutofMemory的数据吗?

如果您使用的是Java 6,请使用jconsole连接到服务器并查看堆。

+0

我会那样做的。但我仍然认为有更好的写作方式...... – TimothyP 2010-06-29 08:27:16

0

如果你读了合同,

InputStream.read():int 

返回-1时,流结束。因此,如果该流不具有0xff字符(例如,如果流在接收到0xff之前结束),则该代码将只将所接收的最后一个字节(例如-1)永远追加到该数组!

修复正是如此:

while (bytes.isEmpty() || bytes.last() != vplEndByte || bytes.last != -1) { 

// ---等问题

虽然你当然应该把这个代码的上限,以避免奥姆斯,您遇到的具体问题是

def bytes = [] //[] is shorthand for "new ArrayList()" 

这产生一个ArrayList,即通过分配的阵列开始,并通过每次达到先前容量增加一倍时其大小增长数组支持数据结构。我相信默认大小只有10个元素。

因此,您在这里隐式创建了大量的数组,这就是栈跟踪混乱的抱怨。在一个高性能的应用程序中,消息的大小为几K到几M,它可能比GC能够更快地分配新阵列。

您应该考虑创建一个合理的初始大小的ArrayList以减少隐式对象实例化并引入一个合理的最大范围。您也可以将该集合切换到链接列表,但可能会降低性能。

相关问题