How to know about OutOfMemory or StackOverflow errors ahead of time

前端 未结 11 1703
余生分开走
余生分开走 2021-02-15 17:10

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

The OutOfMemory exception m

相关标签:
11条回答
  • 2021-02-15 17:39

    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.

    0 讨论(0)
  • 2021-02-15 17:40

    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.

    0 讨论(0)
  • 2021-02-15 17:46

    For StackOverflowError:

    To know the current depth, usually it's either:

    1. using a stateful function (storing the depth in outside the function)
    2. using an accumulator (passing the depth as an argument to the function)

    Knowing the depth it will occur is difficult. There are several factors:

    1. The stack space allocated to the JVM (you can change this with the -Xss option)
    2. The amount of stack space already used
    3. The amount used by the current function.

    Why not try it out using something like this?

    public static void main(String[] args) {
        try {
            recurs();
        } catch (Throwable t) {
            // not a good idea in production code....
        }
    }
    static int depth = 0;
    static void recurs() {
        System.out.println(depth++);
        recurs();
    }
    

    Run it several times. Also try adding dummy variables. It can be seen that even the same code may halt at different depths and adding more variables cause it to end earlier. So yeah, pretty much it's unpredictable.

    I suppose that besides rewriting the algorithm the only option would be to increase the stack space with the -Xss option.

    For OutOfMemoryError, there's the -Xmx option

    0 讨论(0)
  • 2021-02-15 17:47

    You should never see a StackOverflow exception if your application is designed and implemented correctly!

    Generally, if you get a StackOverflow exception then it's a sign that there's a bug in your recursion code.

    0 讨论(0)
  • 2021-02-15 17:51

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题