I need some help with this code which is giving me a heap space issue. I have tried executors too but still I get out of memory issue.
The consists of a queue which initially holds an endpoint. The endpoint gives a response which has child endpoints which is again added to the queue. The response of the endpoint is also written to a file. For every 500 responses written, we change the file.
This is the code:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.stream.Collectors;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
public class Hierarchy {
private static Queue<String> linksQueue = new LinkedList<>();
private static Queue<String> errorQueue = new LinkedList<>();
private static HttpURLConnection connection;
private static URL url;
private static SAXParserFactory factory = SAXParserFactory.newInstance();
private static SAXParser saxParser;
private static XMLHandler xmlHandler = new XMLHandler();
private static FileWriter writer;
private static List<String> urlList = new ArrayList<>();
private static int recordsPerFile = 500;
private static int recordCount = 1;
private static int fileCount = 1;
private static String baseUrl = "parent url here";
public static void main(String[] args) {
String link = null;
try {
saxParser = factory.newSAXParser();
writer = new FileWriter(new File("hierarchy/hierarchy_" + fileCount + ".txt"));
linksQueue.add(baseUrl);
while (!linksQueue.isEmpty()) {
System.out.println("Size:: " + linksQueue.size());
link = linksQueue.poll();
if (link != null) {
System.out.println(link);
linksQueue.addAll(getChildLinks(link));
}
if (recordCount == recordsPerFile) {
doCleanUpForNextFile();
}
}
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void doCleanUpForNextFile() throws IOException, URISyntaxException {
Files.write(Paths.get(new URI("error.txt").toString()), errorQueue, StandardOpenOption.APPEND);
fileCount++;
writer = new FileWriter(new File("hierarchy/hierarchy_" + fileCount + ".txt"));
recordCount = 0;
}
private static List<String> getChildLinks(String link) {
try {
url = new URL(link);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
String result = new BufferedReader(new InputStreamReader(connection.getInputStream())).lines()
.collect(Collectors.joining());
saxParser.parse(new ByteArrayInputStream(result.getBytes()), xmlHandler);
urlList = xmlHandler.getURLList();
writer.write(result + System.lineSeparator());
recordCount++;
} catch (IOException ioe) {
ioe.printStackTrace();
errorQueue.add(link);
} catch (SAXException e) {
e.printStackTrace();
errorQueue.add(link);
}
connection.disconnect();
return urlList;
}
}
This is the XML handler class :
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class XMLHandler extends DefaultHandler {
private List<String> urlList = new ArrayList<String>();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("link") && attributes.getValue(0).equalsIgnoreCase("child")) {
if (attributes.getLength() >= 3) {
this.urlList.add(attributes.getValue(2));
} else {
this.urlList.add(attributes.getValue(1));
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
}
public List<String> getURLList() {
return urlList;
}
}
The problem here is that the queue gets loaded with links at a very high rate while we read only one queue at a time. On average, for every one link polled from the queue, 500 links are added back again to it.
I took a heap dump and found that there are lot of string beings created with white spaces.
These are the issues that I face:
- The queue gets full and heap space issue occurs. (I ran with 2GB of heapspace.)
- The connection gets failed and throws an issue. (It occurs sometimes, don't know what the exact issue is. Will update here.)
- Heap space issue due to something else.
I have been facing with this issue for 1 week now and still can't get to any solution or make this code run without any issues. Please help me to refine this code and make it run without any issues.
doCleanUpForNextFile()
creates a new FileWriter, but the old one is never closed in the main method. And:XMLHandler.urlList
is never cleared, only elements are added. \$\endgroup\$