簡體   English   中英

OutOfMemoryError異常:Java堆空間,如何調試......?

[英]OutOfMemoryError exception: Java heap space, how to debug…?

我得到一個java.lang.OutOfMemoryError異常:Java堆空間。

我正在解析XML文件,在解析完成時存儲數據並輸出XML文件。

我有點驚訝得到這樣的錯誤,因為原始的XML文件根本不長。

代碼: http//d.pr/RSzp文件: http//d.pr/PjrE

可以嘗試在eclipse.ini文件中設置(我假設你使用Eclipse)-Xms和-Xmx值更高。

EX)

-vmargs

-Xms128m //(初始堆大小)

-Xmx256m //(最大堆大小)

如果這是你想要完成的一次性事情,我會嘗試Jason的建議,增加Java可用的內存。

您正在構建一個非常大的對象列表,然后循環遍歷該列表以輸出String,然后將該String寫入文件。 列表和字符串可能是您使用高內存的原因。 您可以以更加面向流的方式重新組織代碼。 在開始時打開文件輸出,然后在解析每個Centroid時為其寫入XML。 然后你不需要保留它們的大列表,並且你不需要持有代表所有XML的大字符串。

轉儲堆並進行分析。 您可以使用-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

簡短回答解釋為什么你有一個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;
        }
    }
}

回答“如何調試”的問題

首先是收集帖子中遺漏的信息。 可能有助於未來人們遇到同樣問題的信息。

首先,完整的堆棧跟蹤。 從XML解析器中拋出的內存不足異常與從代碼中拋出的異常非常不同。

其次,XML文件的大小,因為“不長時間”完全沒用。 是1K,1M還是1G? 有多少元素。

第三,你是如何解析的? SAX,DOM,StAX,完全不同的東西?

第四,你是如何使用這些數據的。 您正在處理一個文件還是多個文件? 解析后是否意外地保留了數據? 代碼示例在這里會有所幫助(並且指向某些第三方網站的鏈接對於未來的SO用戶來說並不是非常有用)。

好吧,我承認我會用一種可能的替代方法來避免你的直接問題。 您可能需要考慮使用XStream進行解析,而不是讓它以較少的代碼處理大部分工作。 下面我粗略的例子用64MB堆解析你的XML。 請注意,它還需要Apache Commons IO才能輕松讀取輸入,以便讓hack將<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;
    }
}

我下載了你的代碼,這是我幾乎從未做過的事情。 我可以肯定地說99%的錯誤是在你的代碼中:循環中的錯誤“if”。 它與Digester或XML沒有任何關系。 要么你犯了一個邏輯錯誤,要么你沒有完全考慮你創造了多少個對象。

但是猜猜:我不會告訴你你的bug是什么。

如果你不能從我上面給出的一些提示中弄清楚,那太糟糕了。 通過在原始帖子中提供足夠的信息來實際開始調試,你把所有其他受訪者都放在了同樣的情況。

也許你應該閱讀 - 實際閱讀 - 我以前的帖子,並用它要求的信息更新你的問題。 或者,如果您不願意這樣做,請接受您的F.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM