[英]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.