Java StackOverflowError в java.io.PrintStream.write (PrintStream.java:480) и дальнейшая трассировка стека - PullRequest
0 голосов
/ 22 октября 2018

Я запускаю программу на Java, написанную другим человеком, на большем количестве данных, чем изначально было задумано, например, в 10 раз длиннее входные файлы, примерно в квадратичном времени выполнения.Я сталкивался с различными проблемами и теперь стремлюсь решать их постепенно и высоко ценю всю помощь, которую я могу получить - большое спасибо за советы!

Во время выполнения, когда большая часть вывода уже напечатана (перенаправлена ​​в файл)Я получаю следующий вывод :

Exception in thread "main" java.lang.StackOverflowError  
        at java.io.PrintStream.write(PrintStream.java:480)  
        [...]  
        at java.io.PrintStream.write(PrintStream.java:480)

Трассировка стека - это первое, что смущает меня, так как это длинное повторение одной и той же строки снова и снова.Кроме того, он не дает намерения, где в коде или выполнении возникла проблема.

Мои мысли / исследования

  • StackOverflowError
    • можетуказать слишком мало памяти.С флагом -Xmx110G я предоставил 110 ГБ памяти и контролировал ее во время выполнения, использовалось только до ~ 32 ГБ.Так что это, вероятно, не проблема здесь.
    • может быть брошено из-за ошибок программирования, вызывающих бесконечный цикл.Тем не менее, я не могу проверить это, так как я недостаточно знаком с кодом, и трассировка стека не помогает мне найти местоположение проблемы в коде.
    • [гипотеза] может быть вызвана, потому что записьвывода медленнее, чем выполнение и новые вызовы печати / записи.Тем не менее, почему нет дальнейшего отслеживания стека?Как я могу проверить и исправить это?
  • PrintStream

    • только фрагменты кода после поиска "PrintStream"

      // reset output stream to suppress the annoying output of the Apache batik library. Gets reset after lib call.  
      OutputStream tmp=System.out;  
      System.setOut(new PrintStream(new org.apache.commons.io.output.NullOutputStream()));  
      drawRes.g2d.stream(new FileWriter(svgFilePath), false);      
      System.setOut(new PrintStream(tmp));  
      
    • [гипотеза] запись в void / null не работает
    • [обходной путь], если пропустить изменение выходного потока и просто «жить» с большим выходом, созданным, программа, кажется, работает(в другие проблемы, но это другой случай).Любые идеи, почему это происходит?

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

Некоторые факты о среде системы

  • Linux-машина
  • 128 G памяти
  • Java

    openjdk version "1.8.0_121"  
    OpenJDK Runtime Environment (IcedTea 3.3.0) (suse-28.1-x86_64)  
    OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
    
  • Пожалуйста, спросите, нужна ли вам дополнительная информация!

Примечания

  • Я довольно новичок в Java, поэтому я благодарен за каждый совет (программа написана не мной)
  • Это мой первый пост о stackoverflow, пожалуйста, сообщите мне, где я могу улучшить свой стиль запроса и форматирования
  • Я не являюсь носителем английского языка, поэтому, пожалуйста, извините за ошибки и не стесняйтесь спрашивать о понимании илипоправьте меня

Спасибо всем за ваши ответы!

1 Ответ

0 голосов
/ 22 октября 2018

Эти две строки выглядят подозрительно:

OutputStream tmp=System.out;
//...
System.setOut(new PrintStream(tmp));

System.out уже является PrintStream, поэтому ИМХО строки должны выглядеть следующим образом:

PrintStream tmp=System.out;
//...
System.setOut(tmp);

В противном случае происходит то, что у вас естьпочти бесконечная упаковка PrintStream с в течение PrintStream с.Вложенность PrintStreams ограничена только пространством кучи Java, но вложение уровня вызова намного ниже.


Чтобы проверить гипотезу, я создал небольшую тестовую программу, которая сначала оборачивает System.out 20 раз и печатаеттрассировка стека для проверки цепочки вызовов.После этого он оборачивается System.out 10_000 раз и создает исключение StackOverflowException.

import java.io.OutputStream;
import java.io.PrintStream;

public class CheckPrintStream {
    public static void main(String[] args) {
        PrintStream originalSystemOut = System.out;
        System.setOut(new PrintStream(System.out) {
            @Override
            public void write(byte buf[], int off, int len) {
                originalSystemOut.write(buf, off, len);
                if (len > 2) {
                    new RuntimeException("Testing PrintStream nesting").printStackTrace(originalSystemOut);
                }
            }
        });

        for (int i = 0; i < 20; i++) {
            wrapSystemOut();
        }
        System.out.println("Hello World!");

        for (int i = 20; i < 10_000; i++) {
            wrapSystemOut();
        }
        System.out.println("crash!");
    }

    private static void wrapSystemOut() {
        OutputStream tmp = System.out;
        System.setOut(new PrintStream(System.out));
    }
}

Для создания переполнения стека достаточно вложения от 6000 до 7000 PrintWriters.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...