1

I'm writing a program that is supposed to continually push generated data into a List sensorQueue. The side effect is that I will eventually run out of memory. When that happens, I'd like drop parts of the list, in this example the first, or older, half. I imagine that if I encounter an OutOfMemeryException, I won't be able to just use sensorQueue = sensorQueue.subList((sensorQueue.size() / 2), sensorQueue.size());, so I came here looking for an answer.

My code:

public static void pushSensorData(String sensorData) {
    try {
        sensorQueue.add(parsePacket(sensorData));
    } catch (OutOfMemoryError e) {
        System.out.println("Backlog full");

        //TODO: Cut the sensorQueue in half to make room
    }
    System.out.println(sensorQueue.size());
}
  • Why do you keep the values in the `sensorQueue` list and not remove the items when you have done your work with the entries? – Progman Jan 18 '20 at 20:29
  • Even trimming down an existing list would require new allocations, so if you are already out of memory you have no guarantee that this would succeed either – UnholySheep Jan 18 '20 at 20:31
  • @Progman The assignment requires to keep all generated data at hand and willfully accepts that the program will run out of memory eventually. I however would like my program to not just crash eventually. – UmBottesWillen Jan 18 '20 at 20:35
  • @UnholySheep Is there an easy way to detect an impending OutOfMemoryException then? Or any other way that would allow me to keep my program running? – UmBottesWillen Jan 18 '20 at 20:36
  • Does this answer your question? [Catching java.lang.OutOfMemoryError?](https://stackoverflow.com/questions/2679330/catching-java-lang-outofmemoryerror) – Progman Jan 18 '20 at 20:41
  • 1
    I'd recommend using `removeRange(from, to)`. It doesn't create another list (modifies current one instead) so will not require additional memory. – Ziumin Jan 18 '20 at 20:42
  • @Ziumin If you feel confident in your suggested solution, could you write a proper answer? Your suggestion would require me to write a custom list that extends List, no? – UmBottesWillen Jan 18 '20 at 20:46
  • Best would be to just stop the program, maybe to try some cleanup before stopping. – Donat Jan 18 '20 at 20:51

4 Answers4

1

Is there an easy way to detect an impending OutOfMemoryException then?

You can have something like below to determine MAX memory and USED memory. Using that information you can define next set of actions in your programme. e.g. reduce its size or drop some elements.

final int MEGABYTE = (1024*1024);
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
long maxMemory = heapUsage.getMax() / MEGABYTE;
long usedMemory = heapUsage.getUsed() / MEGABYTE;

Hope this would helps!

fabfas
  • 2,200
  • 1
  • 21
  • 21
1

The problem with subList is that it creates sublist keeping the original one in memory. However, ArrayList or other extension of AbstractList has removeRange(int fromIndex, int toIndex) which removes elements of current list, so doesn't require additional memory.

For the other List implementations there is similar remove(int index) which you can use multiple times for the same purpose.

Ziumin
  • 4,800
  • 1
  • 27
  • 34
0

I think you idea is severely flawed (sorry).

There is no OutOfMemoryException, there is OutOfMemoryError only! Why that is important? Because errors leaves app in unstable state, well I'm not that sure about that claim in general, but it definitely holds for OutOfMemoryError. Because there is no guarantee, that you will be able to catch it! You can consume all of memory within you try-catch block, and OutOfMemoryError will be thrown somewhere in JDK code. So your catching is pointless.

And what is the reason for this anyways? How many messages do you want in list? Say that your message is 1MB. And your heap is 1000MB. So if we stop considering other classes, your heap size define, that your list will contain up to 1000 messages, right? Wouldn't it be easier to set heap sufficiently big for your desired number of messages, and specify message count in easier, intergral form? And if your answer is "no", then you still cannot catch OutOfMemoryError reliably, so I'd advise that your answer rather should be "yes".

If you really need to consume all what is possible, then checking memory usage in % as @fabsas recommended could be way. But I'd go with integral definition — easier to managed. Your list will contain up-to N messages.

Martin Mucha
  • 2,385
  • 1
  • 29
  • 49
  • + you really want to use proper list implementation for easy trimming of leading items, see some benchmarsk, but maybe https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/list/TreeList.html could be good choice. – Martin Mucha Jan 18 '20 at 21:31
-1

You can drop a range of elements from a ArrayList using subList:

list.subList(from, to).clear();

Where from is the first index of the range to be removed and to is the last. In your case, you can do something like:

list.subList(0, sensorQueue.size() / 2).clear();

Note that this command will return a List.

lffloyd
  • 313
  • 2
  • 8
  • The page you linked to states: *"The returned list is backed by this list"* - which means that the original list is still kept in memory (and contradicts your claim that it removes from the list) – UnholySheep Jan 18 '20 at 20:40
  • I'm pretty sure the sublist is GC'ed and the list is reduced. I just ran a small test. The clear will have effect on the backed list. – Scratte Jan 18 '20 at 21:15
  • @UnholySheep This method is mentioned in Oracle's official Java Tutorial at https://docs.oracle.com/javase/tutorial/collections/interfaces/list.html under "Range-View Operation" saying that "This method eliminates the need for explicit range operations". – Scratte Jan 18 '20 at 21:21
  • Either way, both in the Javadoc (the link in the answer) and in the tutorial, this method is specifically mentioned. As it is a range view I'm quessing it doesn't require a lot of memory either. – Scratte Jan 18 '20 at 21:40