2010-06-26 177 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以減少隱式對象實例化並引入一個合理的最大範圍。您也可以將該集合切換到鏈接列表,但可能會降低性能。

相關問題