代码之家  ›  专栏  ›  技术社区  ›  David Koelle

如何提前知道OutOfMemory或StackOverflow错误

  •  8
  • David Koelle  · 技术社区  · 16 年前

    StackOverflow 错误或 OutOfMemory

    失忆 失忆 异常被抛出。但这些价值观是可知的吗?

    对于 堆栈溢出

    通过提前知道这些错误是否会发生,我觉得我可以更优雅地恢复应用程序,而不是看着它崩溃。

    11 回复  |  直到 16 年前
        1
  •  8
  •   Elijah    16 年前

    预测内存不足错误

    我很惊讶在其他帖子中没有提到这一点,但是你可以使用 ManagementFactory 在Java5/6中获取大量内存使用信息。

    看这张照片 platform mbean server page

        2
  •  5
  •   gustafc    16 年前
        3
  •  3
  •   Uri    16 年前

    大多数StackOverflow错误都来自错误的递归。不幸的是,确定递归是否会停止的问题通常是不可判定的(这是CS中的一个核心概念)。但是,在某些情况下,您可能会收到警告,例如,如果您在无参数的情况下递归调用函数,某些IDE会让您知道。

        4
  •  2
  •   John Topley    16 年前

    你不应该看到一个 StackOverflow

    堆栈溢出 异常则表示递归代码中存在错误。

        5
  •  1
  •   Steve B.    16 年前

    通常记忆的使用很难预测。

    无限回归的堆栈溢出通常在写/调试周期中出现。

    更难发现的是诸如大型保存的集合、缓存等方面的内存问题。正如已经指出的,您可以检查运行时空闲和最大内存,但要小心,因为它们的含义并不明显——“free”在这里表示“free now”,例如,如果您运行完整的gc,您可能会得到更多。不幸的是,如果不运行System.gc(),就无法获得“完全可能的空闲,包括垃圾回收”,这在生产应用程序上不是一件好事(在生产应用程序中,首先可能会有足够大的数据集导致问题),因为整个JVM将突然停止几秒钟请注意,即使System.gc()也不能保证“立即”运行,但我的经验是,每当我使用它时,它都会运行。

    通过使用-verbose:gc、-XX:+printgtimestamps和-XX:+PrintGCDetails(更多详细信息)启动java,可以从运行的jvm打印gc活动 here

        6
  •  1
  •   Tom Hawtin - tackline    16 年前

    你可以做的一件有用的事情就是使用 SoftReference WeakReference 即使在 WeakHashMap 因为当你的应用程序在我身上死掉的时候,我会很生气。

        7
  •  1
  •   Clint    16 年前

    这个 MemoryMXBean 如果内存达到某个阈值,则可以发出通知。

        8
  •  0
  •   Robin    16 年前

    1) 你可以用 Findbugs

    如果这两件事中的任何一件对您来说都是问题,那么解决方案可能不是检测它的发生,而是以不同的方式构建您的解决方案,以尽可能避免它们的发生。

        9
  •  0
  •   Avi    16 年前

    通过创建一个Throwable对象并查询其getStackTrace()方法,您可以发现很多关于递归深度的信息。但这样做很昂贵。

    如果您真的有一个可能引发StackOverflower错误或OutOfMemoryError的方法,为什么不插入try catch块并捕获这些错误呢?它们可以像检查异常一样被捕获和处理。

        10
  •  0
  •   RichN    16 年前

    要了解当前深度,通常需要:

    1. 使用有状态函数(将深度存储在函数外部)

    很难知道它将发生的深度。有几个因素:

    1. 已使用的堆栈空间量
    2. 当前函数使用的量。

    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();
    }
    

    运行几次。还可以尝试添加虚拟变量。可以看出,即使相同的代码也可能在不同的深度停止,添加更多的变量会导致代码提前结束。所以是的,这几乎是不可预测的。

    对于OutOfMemoryError,有-Xmx选项

        11
  •  0
  •   Mike    6 年前

    使用 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);
        }
    }