Исключение OutOfMemoryError: пространство кучи Java, как отладить ...? - PullRequest
3 голосов
/ 24 марта 2011

Я получаю исключение java.lang.OutOfMemoryError: пространство кучи Java.

Я анализирую XML-файл, сохраняю данные и выводю XML-файл, когда анализ завершен.

Я немного удивлен, получив такую ​​ошибку, потому что оригинальный XML-файл совсем не длинный.

Код: http://d.pr/RSzp Файл: http://d.pr/PjrE

Ответы [ 7 ]

2 голосов
/ 24 марта 2011

Сбросьте кучу и проанализируйте ее.Вы можете настроить автоматический дамп кучи при ошибке памяти, используя системное свойство -XX:+HeapDumpOnOutOfMemoryError.

http://www.oracle.com/technetwork/java/javase/index-137495.html

https://www.infoq.com/news/2015/12/OpenJDK-9-removal-of-HPROF-jhat

http://blogs.oracle.com/alanb/entry/heap_dumps_are_back_with

2 голосов
/ 25 марта 2011

Короткий ответ, чтобы объяснить, почему у вас есть ошибка OutOfMemoryError, для каждого центроида, найденного в файле, вы перебираете уже «зарегистрированные» центроиды, чтобы проверить, известен ли он уже (добавить новый или обновить уже зарегистрированный) , Но для каждого неудачного сравнения вы добавляете новую копию нового центроида. Таким образом, для каждого нового центроида он добавляет его столько раз, сколько центроидов в списке уже есть, затем вы встречаете первый добавленный вами, обновляете его и покидаете цикл ...

Вот некоторый переработанный код:

public class CentroidGenerator {

    final Map<String, Centroid> centroids = new HashMap<String, Centroid>();

    public Collection<Centroid> getCentroids() {
        return centroids.values();
    }

    public void nextItem(FlickrDoc flickrDoc) {

        final String event = flickrDoc.getEvent();
        final Centroid existingCentroid = centroids.get(event);

        if (existingCentroid != null) {
            existingCentroid.update(flickrDoc);
        } else {
            final Centroid newCentroid = new Centroid(flickrDoc);
            centroids.put(event, newCentroid);
        }
    }


    public static void main(String[] args) throws IOException, SAXException {

        // instantiate Digester and disable XML validation
        [...]


        // now that rules and actions are configured, start the parsing process
        CentroidGenerator abp = (CentroidGenerator) digester.parse(new File("PjrE.data.xml"));

        Writer writer = null;

        try {
            File fileOutput = new File("centroids.xml");
            writer = new BufferedWriter(new FileWriter(fileOutput));
            writeOuput(writer, abp.getCentroids());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private static void writeOuput(Writer writer, Collection<Centroid> centroids) throws IOException {

        writer.append("<?xml version='1.0' encoding='utf-8'?>" + System.getProperty("line.separator"));
        writer.append("<collection>").append(System.getProperty("line.separator"));

        for (Centroid centroid : centroids) {
            writer.append("<doc>" + System.getProperty("line.separator"));

            writer.append("<title>" + System.getProperty("line.separator"));
            writer.append(centroid.getTitle());
            writer.append("</title>" + System.getProperty("line.separator"));

            writer.append("<description>" + System.getProperty("line.separator"));
            writer.append(centroid.getDescription());
            writer.append("</description>" + System.getProperty("line.separator"));

            writer.append("<time>" + System.getProperty("line.separator"));
            writer.append(centroid.getTime());
            writer.append("</time>" + System.getProperty("line.separator"));

            writer.append("<tags>" + System.getProperty("line.separator"));
            writer.append(centroid.getTags());
            writer.append("</tags>" + System.getProperty("line.separator"));

            writer.append("<geo>" + System.getProperty("line.separator"));

            writer.append("<lat>" + System.getProperty("line.separator"));
            writer.append(centroid.getLat());
            writer.append("</lat>" + System.getProperty("line.separator"));

            writer.append("<lng>" + System.getProperty("line.separator"));
            writer.append(centroid.getLng());
            writer.append("</lng>" + System.getProperty("line.separator"));

            writer.append("</geo>" + System.getProperty("line.separator"));

            writer.append("</doc>" + System.getProperty("line.separator"));

        }
        writer.append("</collection>" + System.getProperty("line.separator") + System.getProperty("line.separator"));

    }

    /**
     * JavaBean class that holds properties of each Document entry. It is important that this class be public and
     * static, in order for Digester to be able to instantiate it.
     */
    public static class FlickrDoc {
        private String id;
        private String title;
        private String description;
        private String time;
        private String tags;
        private String latitude;
        private String longitude;
        private String event;

        public void setId(String newId) {
            id = newId;
        }

        public String getId() {
            return id;
        }

        public void setTitle(String newTitle) {
            title = newTitle;
        }

        public String getTitle() {
            return title;
        }

        public void setDescription(String newDescription) {
            description = newDescription;
        }

        public String getDescription() {
            return description;
        }

        public void setTime(String newTime) {
            time = newTime;
        }

        public String getTime() {
            return time;
        }

        public void setTags(String newTags) {
            tags = newTags;
        }

        public String getTags() {
            return tags;
        }

        public void setLatitude(String newLatitude) {
            latitude = newLatitude;
        }

        public String getLatitude() {
            return latitude;
        }

        public void setLongitude(String newLongitude) {
            longitude = newLongitude;
        }

        public String getLongitude() {
            return longitude;
        }

        public void setEvent(String newEvent) {
            event = newEvent;
        }

        public String getEvent() {
            return event;
        }
    }

    public static class Centroid {
        private final String event;
        private String title;
        private String description;

        private String tags;

        private Integer time;
        private int nbTimeValues = 0; // needed to calculate the average later

        private Float latitude;
        private int nbLatitudeValues = 0; // needed to calculate the average later
        private Float longitude;
        private int nbLongitudeValues = 0; // needed to calculate the average later

        public Centroid(FlickrDoc flickrDoc) {
            event = flickrDoc.event;
            title = flickrDoc.title;
            description = flickrDoc.description;
            tags = flickrDoc.tags;
            if (flickrDoc.time != null) {
                time = Integer.valueOf(flickrDoc.time.trim());
                nbTimeValues = 1; // time is the sum of one value
            }            
            if (flickrDoc.latitude != null) {
                latitude = Float.valueOf(flickrDoc.latitude.trim());
                nbLatitudeValues = 1; // latitude is the sum of one value
            }
            if (flickrDoc.longitude != null) {
                longitude = Float.valueOf(flickrDoc.longitude.trim());
                nbLongitudeValues = 1; // longitude is the sum of one value
            }
        }

        public void update(FlickrDoc newData) {
            title = title + " " + newData.title;
            description = description + " " + newData.description;
            tags = tags + " " + newData.tags;
            if (newData.time != null) {
                nbTimeValues++;
                if (time == null) {
                    time = 0;
                }
                time += Integer.valueOf(newData.time.trim());
            }
            if (newData.latitude != null) {
                nbLatitudeValues++;
                if (latitude == null) {
                    latitude = 0F;
                }
                latitude += Float.valueOf(newData.latitude.trim());
            }
            if (newData.longitude != null) {
                nbLongitudeValues++;
                if (longitude == null) {
                    longitude = 0F;
                }
                longitude += Float.valueOf(newData.longitude.trim());
            }
        }

        public String getTitle() {
            return title;
        }

        public String getDescription() {
            return description;
        }

        public String getTime() {
            if (nbTimeValues == 0) {
                return null;
            } else {
                return Integer.toString(time / nbTimeValues);
            }
        }

        public String getTags() {
            return tags;
        }

        public String getLat() {
            if (nbLatitudeValues == 0) {
                return null;
            } else {
                return Float.toString(latitude / nbLatitudeValues);
            }
        }

        public String getLng() {
            if (nbLongitudeValues == 0) {
                return null;
            } else {
                return Float.toString(longitude / nbLongitudeValues);
            }
        }

        public String getEvent() {
            return event;
        }
    }
}
2 голосов
/ 24 марта 2011

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

Вы создаете очень большой список объектов и затем просматриваете этот список, чтобы вывести строку, а затем записываете эту строку в файл. Список и строка, вероятно, являются причинами высокого использования памяти. Вы можете реорганизовать свой код более ориентированным на поток способом. Вначале откройте файл вывода, а затем запишите XML для каждого Centroid по мере их анализа. Тогда вам не нужно будет хранить большой список из них, и вам не нужно будет хранить большую строку, представляющую весь XML.

2 голосов
/ 24 марта 2011

Можно попытаться установить значения (я предполагаю, что вы используете Eclipse) -Xms и -Xmx в файле eclipse.ini.

ех)

-vmargs

-Xms128m // (начальный размер кучи)

-Xmx256m // (максимальный размер кучи)

0 голосов
/ 25 марта 2011

Я скачал твой код, что я почти никогда не делаю.И я могу сказать с уверенностью 99%, что ошибка в вашем коде: неверное «если» внутри цикла.Он не имеет ничего общего с Digester или XML.Либо вы допустили логическую ошибку, либо не до конца продумали, сколько объектов вы создадите.

Но угадайте, что: я не собираюсь рассказывать вам, в чем ваша ошибка.

Если вы не можете понять это из нескольких подсказок, которые я дал выше, слишком плохо.Это та же самая ситуация, в которой вы опускаете всех остальных респондентов, не предоставив достаточно информации - в исходном посте - чтобы фактически начать отладку.

Возможно, вам следует прочитать - на самом деле прочитайте - мой прежний пост и обновите ваш вопрос информацией, которую он запрашивает.Или, если вас это не затруднит, примите ваш F.

0 голосов
/ 24 марта 2011

Хорошо, я признаю, что избегаю вашего прямого вопроса с возможной альтернативой. Возможно, вы захотите рассмотреть синтаксический анализ с XStream , чтобы позволить ему справляться с большей частью работы с меньшим количеством кода. Мой грубый пример ниже анализирует ваш XML с кучей 64 МБ. Обратите внимание, что для этого требуется Apache Commons IO, просто чтобы легко прочитать вводные данные, чтобы хак превратил <collection> в <list>.

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;

public class CentroidGenerator {
    public static void main(String[] args) throws IOException {
        for (Centroid centroid : getCentroids(new File("PjrE.data.xml"))) {
            System.out.println(centroid.title + " - " + centroid.description);
        }
    }

    @SuppressWarnings("unchecked")
    public static List<Centroid> getCentroids(File file) throws IOException {
        String input = FileUtils.readFileToString(file, "UTF-8");
        input = input.replaceAll("collection>", "list>");

        XStream xstream = new XStream();
        xstream.processAnnotations(Centroid.class);

        Object output = xstream.fromXML(input);
        return (List<Centroid>) output;
    }

    @XStreamAlias("doc")
    @SuppressWarnings("unused")
    public static class Centroid {
        private String id;
        private String title;
        private String description;
        private String time;
        private String tags;
        private String latitude;
        private String longitude;
        private String event;
        private String geo;
    }
}
0 голосов
/ 24 марта 2011

Ответ на вопрос «Как отлаживать»

Все начинается с сбора информации, которая отсутствует в вашем посте.Информация, которая потенциально может помочь будущим людям, имеющим ту же проблему.

Во-первых, полная трассировка стека.Исключение нехватки памяти, которое выдается из синтаксического анализатора XML, очень отличается от того, которое выдается из вашего кода.

Во-вторых, размер файла XML, потому что «совсем недолго» совершенно бесполезен.Это 1K, 1M или 1G?Сколько элементов.

В-третьих, как вы анализируете?SAX, DOM, StAX, что-то совершенно другое?

В-четвертых, как вы используете данные.Вы обрабатываете один файл или несколько файлов?Вы случайно держитесь за данные после разбора?Здесь может помочь пример кода (а ссылка на какой-либо сторонний сайт не очень полезна для будущих пользователей SO).

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