How to know about OutOfMemory or StackOverflow err

2020-06-16 02:46发布

In Java, is there a way to know that a StackOverflow error or OutOfMemory exception may happen soon?

The OutOfMemory exception might be an easier one to catch, if one is capable of getting memory usage statistics programmatically, and if one knows ahead of time how much memory needs to be used before the OutOfMemory exception is thrown. But are those values knowable?

For the StackOverflow error, is there a way to get recursion depth, and how does one know what value for recursion depth would cause the error to occur?

By knowing ahead of time whether these errors will happen, I feel I can recover the application more gracefully, instead of watching it crash.

11条回答
疯言疯语
2楼-- · 2020-06-16 03:01

Usually memory useage is hard to predict.

Stack Overflows from infinite regression generally show up in the write/debug cycle.

Harder to catch are memory issues on things like large held collections, caches, etc.etc. As has been pointed out you can check runtime free and max memory, but be careful, as their meanings aren't obvious - "free" here, means "free now", e.g. meaning if you ran a full gc you might get more. Unfortunately there's no way to get the "total possible free, including garbage-collectible" without running System.gc(), which is not a nice thing to do on a production application (where you're liable to have large enough data sets to cause the problem in the first place) because the entire JVM will come to a screeching halt for a few seconds (or more, in a large app). Note that even System.gc() is not guaranteed to run "now", but my experience has been that it has whenever I've played with it.

You can print gc activity from the running jvm by starting java with -verbose:gc, -XX:+PrintGCTimeStamps, and -XX:+PrintGCDetails (more detail here), and in general if the collector starts to run more frequently, it's probably a sign that you're running out of memory.

查看更多
Ridiculous、
3楼-- · 2020-06-16 03:04

You can anticipate out-of-memory conditions with Runtime.freeMemory() and Runtime.maxMemory(). Most times it'll be hard recovering gracefully, but I leave that to you.

查看更多
干净又极端
4楼-- · 2020-06-16 03:09

I don't know anything about working this out at run time, or what you might be able to do to avoid it once you predict it is going to happen. Better to try and avoid it occurring in the first place.

1) You could use Findbugs which may indicate some StackOverFlow errors occurring from inadvertently calling the same method from itself.

2) You could store data likely to cause you to run out of memory with a SoftReference and have a null check on accessing it so it can be reloaded if it's been garbage collected.

If either of these things are actually issues for you then the solution probably isn't in detecting it happening but architecting your solution differently to avoid them occuring if at all possible.

查看更多
贪生不怕死
5楼-- · 2020-06-16 03:10

Most StackOverflow errors come out of bad recursion. Unfortunately, the problem of determining if a recursion will stop is generally not decidable (this is a central concept in CS). There are cases, however, you could get warnings, for example some IDEs will let you know if you're invoking a function recursively with no parameters.

查看更多
聊天终结者
6楼-- · 2020-06-16 03:13

One useful thing you can do is use SoftReferences for caches. That will give you a gradual performance slide as you run out of memory. Just don't use WeakReference even in a WeakHashMap because it will annoy me when your application dies nastily on me.

查看更多
我欲成王,谁敢阻挡
7楼-- · 2020-06-16 03:15

Implementation example using MemoryPoolMXBean

import static java.lang.management.ManagementFactory.getMemoryMXBean;
import static java.lang.management.ManagementFactory.getMemoryPoolMXBeans;
import static java.lang.management.MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED;
import static java.lang.management.MemoryType.HEAP;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MemoryWatcher {

    public interface Listener {
        void memoryUsageLow(long usedMemory, long maxMemory);
    }

    private static final MemoryPoolMXBean tenuredGenPool = findTenuredGenPool();
    private static final Logger logger = LoggerFactory.getLogger(MemoryWatcher.class);

    private static MemoryPoolMXBean findTenuredGenPool() {
        for (MemoryPoolMXBean pool : getMemoryPoolMXBeans())
            if (pool.getType() == HEAP && pool.isUsageThresholdSupported())
                return pool;
        return null;
    }

    public static MemoryPoolMXBean getTenuredGenPool() {
        return tenuredGenPool;
    }

    private final Collection<Listener> listeners = new CopyOnWriteArrayList<>();

    public MemoryWatcher(double usageThresholdPercent) {
        if (tenuredGenPool == null) {
            logger.warn("Tenured pool is not used");
            return;
        }
        if (tenuredGenPool.getUsageThreshold() != 0)
            logger.warn("Overriding tenured usage threshold {} with {}", tenuredGenPool.getUsage().getMax() / (double) tenuredGenPool.getUsageThreshold(), usageThresholdPercent);
        tenuredGenPool.setUsageThreshold((long) (tenuredGenPool.getUsage().getMax() * usageThresholdPercent));

        NotificationEmitter emitter = (NotificationEmitter) getMemoryMXBean();
        emitter.addNotificationListener((Notification n, Object hb) -> {
            MemoryUsage usage = tenuredGenPool.getUsage();
            if (n.getType().equals(MEMORY_THRESHOLD_EXCEEDED) && usage.getMax() == usage.getCommitted())
                listeners.forEach(listener -> listener.memoryUsageLow(usage.getUsed(), usage.getMax()));
        }, null, null);
    }

    public boolean addListener(Listener listener) {
        return listeners.add(listener);
    }

    public boolean removeListener(Listener listener) {
        return listeners.remove(listener);
    }
}
查看更多
登录 后发表回答