I want to format and write the contents of a large Map
(1.785.530 entries) to a text file. After about 85% of the entries are processed, it gets very slow and then I get an OutOfMemoryException
.
This same error occurs, even if I:
- periodically call
flush()
orclose()
on myFileWriter
- use
BufferedWriter
- write each line to a
StringBuffer
before writing to the file
This is my code:
private static final TreeMap<Date, Integer> accessesPerSecondMap = new
TreeMap<>();
...
private static void writeOutputFile() throws IOException {
FileWriter writer = new FileWriter(FILENAME_OUTPUT);
writer.write("Date");
writer.write(',');
writer.write("Request Count");
writer.write('\n');
for (Date date : accessesPerSecondMap.keySet()) {
// first and last date are not precise so do not write it in the
// file
if (date == accessesPerSecondMap.firstKey()
|| date == accessesPerSecondMap.lastKey()) {
continue;
}
writer.write(String.valueOf(date));
System.out.println("FileMerger wrote: " + String.valueOf(date));
writer.write(',');
writer.write(String.valueOf(accessesPerSecondMap.get(date)));
writer.write('\n');
}
writer.flush();
writer.close();
}
And here is the exception that is thrown:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at sun.util.resources.TimeZoneNames.getContents(Unknown Source)
at sun.util.resources.OpenListResourceBundle.loadLookup(Unknown Source)
at sun.util.resources.OpenListResourceBundle.loadLookupTablesIfNecessary(Unknown Source)
at sun.util.resources.OpenListResourceBundle.handleKeySet(Unknown Source)
at java.util.ResourceBundle.containsKey(Unknown Source)
at sun.util.locale.provider.LocaleResources.getTimeZoneNames(Unknown Source)
at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayNameArray(Unknown Source)
at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayName(Unknown Source)
at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getName(Unknown Source)
at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(Unknown Source)
at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(Unknown Source)
at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(Unknown Source)
at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(Unknown Source)
at sun.util.locale.provider.TimeZoneNameUtility.retrieveDisplayName(Unknown Source)
at java.util.TimeZone.getDisplayName(Unknown Source)
at java.util.Date.toString(Unknown Source)
at java.lang.String.valueOf(Unknown Source)
at FileMerger.writeOutputFile(FileMerger.java:95)
at FileMerger.main(FileMerger.java:26)
Increasing the heap space works for me, but is not a really satisfying solution in my opinion. In a few days I will have to write files that are twice as big; hope the space is enough for them too.
I'm running Java 1.8.0_45 on Windows. The code above is the real code, and the only code running. For testing purposes I use the following code for filling the TreeMap
:
int accesses = 43267;
for (int i = 0; i < 1785530; i++) {
Date date = new Date(i);
accessesPerSecondMap.put(date, accesses);
}
I've added the following to the for-loop to track the memory usage:
...
int count = 100000;
for (Date date : accessesPerSecondMap.keySet()) {
if (count == 100000) {
count = 0;
printOutMemoryUsage();
}
count++;
...
private void printOutMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
NumberFormat format = NumberFormat.getInstance();
StringBuilder sb = new StringBuilder();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
sb.append("free memory: " + format.format(freeMemory / 1024) + " ");
sb.append("allocated memory: " + format.format(allocatedMemory / 1024)
+ " ");
sb.append("max memory: " + format.format(maxMemory / 1024) + " ");
sb.append("total free memory: "
+ format.format((freeMemory + (maxMemory - allocatedMemory)) / 1024)
+ "<br/>");
System.out.println(sb);
}
I am getting this output when I don't increase the heap space:
free memory: 28.868 allocated memory: 156.792 max memory: 253.440 total free memory: 125.516
free memory: 89.847 allocated memory: 253.440 max memory: 253.440 total free memory: 89.847
free memory: 87.796 allocated memory: 253.440 max memory: 253.440 total free memory: 87.796
free memory: 89.758 allocated memory: 253.440 max memory: 253.440 total free memory: 89.758
free memory: 32.478 allocated memory: 253.440 max memory: 253.440 total free memory: 32.478
free memory: 35.182 allocated memory: 253.440 max memory: 253.440 total free memory: 35.182
free memory: 37.269 allocated memory: 253.440 max memory: 253.440 total free memory: 37.269
free memory: 45.165 allocated memory: 253.440 max memory: 253.440 total free memory: 45.165
free memory: 42.943 allocated memory: 253.440 max memory: 253.440 total free memory: 42.943
free memory: 32.055 allocated memory: 253.440 max memory: 253.440 total free memory: 32.055
free memory: 13.053 allocated memory: 253.440 max memory: 253.440 total free memory: 13.053
free memory: 14.281 allocated memory: 253.440 max memory: 253.440 total free memory: 14.281
free memory: 12.797 allocated memory: 253.440 max memory: 253.440 total free memory: 12.797
free memory: 1.973 allocated memory: 253.440 max memory: 253.440 total free memory: 1.973
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
With increased heapspace -Xms512m -Xmx1230m
i get:
free memory: 372.630 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.083.350
free memory: 329.031 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.039.751
free memory: 273.121 allocated memory: 506.816 max memory: 1.217.536 total free memory: 983.841
free memory: 333.700 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.044.420
free memory: 276.392 allocated memory: 506.816 max memory: 1.217.536 total free memory: 987.112
free memory: 220.482 allocated memory: 506.816 max memory: 1.217.536 total free memory: 931.202
free memory: 279.896 allocated memory: 506.816 max memory: 1.217.536 total free memory: 990.616
free memory: 223.986 allocated memory: 506.816 max memory: 1.217.536 total free memory: 934.706
free memory: 284.565 allocated memory: 506.816 max memory: 1.217.536 total free memory: 995.285
free memory: 228.654 allocated memory: 506.816 max memory: 1.217.536 total free memory: 939.374
free memory: 169.949 allocated memory: 506.816 max memory: 1.217.536 total free memory: 880.669
free memory: 230.528 allocated memory: 506.816 max memory: 1.217.536 total free memory: 941.248
free memory: 174.618 allocated memory: 506.816 max memory: 1.217.536 total free memory: 885.338
free memory: 235.197 allocated memory: 506.816 max memory: 1.217.536 total free memory: 945.917
free memory: 179.287 allocated memory: 506.816 max memory: 1.217.536 total free memory: 890.007
free memory: 123.376 allocated memory: 506.816 max memory: 1.217.536 total free memory: 834.096
free memory: 183.956 allocated memory: 506.816 max memory: 1.217.536 total free memory: 894.676
free memory: 128.046 allocated memory: 506.816 max memory: 1.217.536 total free memory: 838.766
FileMerger main method finished
TreeMap
of dates just happens to take up almost all of your JVM's memory. You should try, before your output loop, running garbage collection and checking how much free memory your JVM has. See https://mcmap.net/q/194610/-how-do-i-check-cpu-and-memory-usage-in-java/3004881 – StoltzloadLookup()
) that I'd think wouldn't need to happen in the middle of the program. I wonder if you could fix or get around this problem by (a) using a newer JVM, (b) storing strings or millisecondLong
s in yourTreeMap
instead ofDate
s, and/or (c) printing time stamps using a custom output format (SimpleDateFormat
) instead of using the default format. (And even ifBufferedWriter
has nothing to do with your problem, it's a good idea for your program, if you didn't realize yet.) – Stoltz