Почему этот код Java / Groovy вызывает исключения кучи памяти? - PullRequest
1 голос
/ 26 июня 2010

Этот сторонний скрипт продолжает вызывать исключения кучи памяти:

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 после получения сообщения, вызывает следующее исключение:

Исключение в потоке "Thread-2" org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.OutOfMemoryError: пространство кучи Java в org.codehaus.groovy.reflection.CachedMethod.invoke (CachedMethod.j92: CachedMethod.j)at groovy.lang.MetaMethod.doMethodInvoke (MetaMethod.java:234) в org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod (ClosureMetaClass.java:272) Megavlox.) в groovy.lang.Closure.call (Closure.java:279) в groovy.lang.Closure.call (Closure.java:292) в org.codehaus.groovy.runtime.DefaultGroovyMethods $ 6.run (DefaultGroovyMethods.java:11563)) в java.lang.Thread.run (Thread.java:636) Причина: java.lang.OutOfMemoryError: пространство кучи Java в java.util.Arrays.copyOf (Arrays.java:2746) в java.util.ArrayList.ensureCapacity (ArrayList.java:187) в java.util.ArrayList.add (ArrayList.java:378) в sun.reflect.GeneratedMethodAccessor9.invoke (неизвестный источник) в sun.reflect.DelegatingMethhodAccessorImpj43) at java.lang.reflect.Method.invoke (Method.java:616) по адресу org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite $ PojoCachedMethodSiteNoUnwrapNoCoerce.invoke (Pojaug.jov..callsite.PojoMetaMethodSite.call (PojoMetaMethodSite.java:52) в org.codehaus.groovy.runtime.callsite.AbstractCallSite.call (AbstractCallSite.java:125) в RTSGatewayServer.rever.RateRateRateRateRateRateRateRateRateRateRateRateNativeMethodAccessorImpl.invoke0 (родной метод) в sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:57) в sun.reflect.DelegatingMethodAccessorImpl.invoke (methodava.tho.jj.jjJava: 616) в org.codehaus.groovy.reflection.CachedMethod.вызывать (CachedMethod.java:86) в groovy.lang.MetaMethod.doMethodInvoke (MetaMethod.java:234) в org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod.lolassMeglass.etaglog.tag1.glog.tag.invokeMethod (MetaClassImpl.java:880) в org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent (PogoMetaClassSite.java:66) в org.codehaus.groovy.runtime.callSite.AciteSite.Aj) на сервере RTSGatewayServerDelegatingMethodAccessorImpl.java:43) в java.lang.reflect.Method.invoke (Method.java:616) в org.codehaus.groovy.reflection.CachedMethod.invoke (CachedMethod.java:86) в groovy.lang.hodModek(MetaMethod.java:234) в org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod (ClosureMetaClass.java:272) в groovy.lang.MetaClassImpl.invokeMethod (MetaClassImpl.java:880) в groovy.lang.Closure.call (Closure.java:279) в org.efovgr..withStreams (DefaultGroovyMethods.java:11462) в org.codehaus.groovy.runtime.dgm $ 658.invoke (Неизвестный источник)

Я предполагаю, что есть более эффективный способ памяти, чем при использовании bytes.add(...)?

Если кто-нибудь может сравнить результат с тем, что произойдет в .NET, было бы еще лучше, поскольку я являюсь разработчиком .NET.

Ответы [ 4 ]

3 голосов
/ 26 июня 2010

Таким образом, скрипт продолжает читать и хранить байты, пока не увидит 0xff.

Это похоже на ошибочный дизайн. Независимо от того, как вы настраиваете JVM, у вас потенциально может закончиться память. Если удаленный сервис выберет отправку peta-байтов deta до 0xff, то вы исчерпаете память. Я считаю, что вы всегда должны предполагать, что другой участник может быть сломлен или антисоциальн.

Поэтому я бы установил некоторую верхнюю границу того, сколько данных я готов принять. Затем, если возможно, разберитесь с полученным чанком и вернитесь и получите следующий чанк, или если невозможно обработать чанки, вежливо выведите сообщение об ошибке и остановитесь.

Итог: дезинфицировать ваши входные данные. Позволить внешнему процессу исчерпать вашу память - это плохо. Не верьте им, когда они говорят: «Этого не может быть».

1 голос
/ 28 июня 2010

Действительно ли данные из Socket вызывают OutofMemory?

Если вы используете Java 6, используйте jconsole для подключения к серверу и посмотрите на кучу.

1 голос
/ 26 июня 2010

Настройте JVM с - Xmx ...?

0 голосов
/ 25 июня 2015

Если вы читаете контракт,

InputStream.read():int 

возвращает -1, когда заканчивается поток.

Таким образом, если поток не содержит символ 0xff (например, еслипоток заканчивается до получения 0xff), этот код будет просто добавлять последний полученный байт (например, -1) к этому массиву навсегда!

Исправить таким образом:

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

// --проблемы

Несмотря на то, что вы должны обязательно ставить верхние границы этого кода, чтобы избежать OOM, конкретная проблема, с которой вы столкнулись, связана с

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

. Это создает ArrayList, массив с поддержкойструктура данных, которая начинается с выделения массива и увеличивается путем удвоения его размера при каждом достижении предыдущей емкости.Я считаю, что размер по умолчанию составляет всего 10 элементов.

Таким образом, вы неявно создаете здесь огромное количество массивов, на что жалуется беспорядок трассировки стека.В высокопроизводительном приложении с сообщениями небольшого размера от нескольких K до M, скорее всего, вы выделяете новые массивы быстрее, чем может выдержать GC.

Вы должны рассмотреть возможность создания этого массива с разумным начальным размером дляуменьшить неявную реализацию объекта и ввести разумные максимальные границы.Вы также можете переключить эту коллекцию в связанный список, но у вас может снизиться производительность.

...