2009-08-27 21 views
2

我的Java程序突然退出,没有任何异常抛出或程序正常完成时出现问题。Java虚拟机突然退出没有明显的原因

我正在写一个程序来解决Project Euler14th problem。这是我的了:

private static final int INITIAL_CACHE_SIZE = 30000; 
private static Map<Long, Integer> cache = new HashMap<Long, Integer>(INITIAL_CACHE_SIZE); 

public void main(String... args) { 
    long number = 0; 
    int maxSize = 0; 

    for (long i = 1; i <= TARGET; i++) { 
     int size = size(i); 
     if (size > maxSize) { 
      maxSize = size; 
      number = i; 
     } 
    } 
} 
private static int size(long i) { 
    if (i == 1L) { 
     return 1; 
    } 
    final int size = size(process(i)) + 1; 
    return size; 
} 

private static long process(long n) { 
    return n % 2 == 0 ? n/2 : 3*n + 1; 
} 

这运行正常,并且使用的1 000 000

的目标,我想通过增加高速缓存优化时,约5秒钟正确完成,所以我改变了大小的方法是:

private static int size(long i) { 
    if (i == 1L) { 
     return 1; 
    } 
    if (cache.containsKey(i)) { 
     return cache.get(i); 
    } 
    final int size = size(process(i)) + 1; 
    cache.put(i, size); 
    return size; 
} 

现在,当我运行它,它只是停止(进程退出)当我每一次555144.相同的号码。没有异常,错误,Java VM崩溃或任何事情被抛出。

更改缓存的大小似乎不具有任何效力,要么,怎么可能缓存 出台导致这个错误?

如果我执行高速缓存的大小是不只是初步的,但永久性像这样:

if (i < CACHE_SIZE) { 
     cache.put(i, size); 
    } 

的错误不再出现。 编辑:当我将缓存大小设置为2M时,该错误再次开始显示。

任何人都可以重现这一点,甚至可以提供一个建议,为什么会发生?

+0

你在运行什么操作系统? – 2009-08-27 21:14:21

+0

我在Windows Vista Business和JDK 1.6.0_03上运行 – Jorn 2009-08-27 21:16:40

+0

您可能想要尝试更新jdk并查看是否获得相同的行为。他们现在正在更新16到1.6。 – digitaljoel 2009-08-27 21:26:32

回答

8

这仅仅是一个不被打印一个OutOfMemoryError。如果我设置了很高的堆大小,程序运行良好,否则它将以未记录的OutOfMemoryError退出(尽管在调试器中很容易看到)。

您可以验证这一点,并通过将这个JVM ARG得到一个堆转储(以及作为一个OutOfMemoryError发生打印),并重新运行程序:

-XX:+HeapDumpOnOutOfMemoryError

有了这个,然后它会打印出的东西这种效果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid4192.hprof ...
Heap dump file created [91901809 bytes in 4.464 secs]

提高你堆的大小有,比方说,-Xmx200m,你会不会有一个问题 - 至少TARGET = 1000000。

+0

是的,你说得对。该命令行arg确实显示了OutOfMemoryError。 – Jorn 2009-08-27 22:53:49

0

如果你的java进程突然崩溃,它可能是一些资源得到了最大化。像记忆一样。你可以尝试设置更高的最大堆

+0

我不认为就是这样。它不会抛出OutOfMemoryExceptions。此外,Windows任务管理器告诉我它没有使用超过100 MB的内存。 – Jorn 2009-08-27 21:15:30

+0

它可能没有使用超过100MB,但是当它试图分配一些非常大的内存块时(它不会显示在任务管理器中,因为它从不分配),它会保存。您关于缺乏OutOfMemoryExceptions的观点并不完全适合这种情况,尽管... – rmeador 2009-08-27 21:26:15

+1

我已经试过了。我可以重现它。 Java exe进程将错误代码1返回给操作系统。我怀疑hashmap的重新散列有一个bug。我在Sun的JDK 1.6.0_16 – 2009-08-27 21:39:39

0

你看到崩溃后产生堆转储?这个文件应该在你的JVM的当前目录中,这是我寻找更多信息的地方。

+0

没有,没有。 – Jorn 2009-08-27 21:29:59

+0

堆转储不会在不通过arg的情况下自动生成。可能还有其他方法,但在这种情况下,通过:-XX:+ HeapDumpOnOutOfMemoryError – 2009-08-27 22:09:03

+0

堆转储是自动生成的(尽管虚拟机实现之间的行为可能非常相似),而不是针对OutOfMemoryErrors,而不是针对真正的jvm崩溃。 – Yishai 2009-08-28 04:26:05

0

我在cache.put(i,size)上发生OutOfMemory错误;

为了让错误在Eclipse中使用它会出现在调试窗口调试模式下运行程序。它不会在控制台中生成堆栈跟踪。

3

这听起来像JVM本身崩溃(即第一个念头时,你的程序死亡,没有例外的暗示反正)。这个问题的第一步是升级到您的平台的最新版本。 JVM应该将堆转储到您启动JVM的目录中的.log文件,假设您的用户级别具有对该目录的访问权限。

这就是说,一些OutOfMemory错误不会在主线程中报告,所以除非你做了try/catch(Throwable t)并且看看你是否得到了它,否则很难确定你是不是真的只是内存不足而已。它仅使用100MB的事实可能意味着JVM未配置为使用更多。可以通过将启动选项更改为JVM到-Xmx1024m来获得内存Gig,以查看问题是否存在。

做的尝试捕捉代码应该是这样的:

public static void main(String[] args) { 
    try { 
     MyObject o = new MyObject(); 
     o.process(); 
    } catch (Throwable t) { 
     t.printStackTrace(); 
    } 
} 

在这个过程中方法做的一切,你的缓存如果错误发生在catch语句不存储在静态,这样,该对象超出范围,可以进行垃圾回收,释放足够的内存以允许打印堆栈跟踪。没有保证,这有效,但它给了它一个更好的镜头。

+0

这个问题是一个OutOfMemoryError,可能在一个线程中,一旦它超过一定的大小,它就重新哈希散列图。 – 2009-08-27 21:56:21

+0

这不应该发生在一个单独的线程中。 – 2009-08-27 22:03:29

0

递归大小()方法可能不是一个好的地方做缓存。我打电话给cache.put(我,尺寸);在main()的for循环中,它的工作速度要快得多。否则,我也会遇到OOM错误(不再有堆空间)。

编辑:这是源代码 - 缓存检索的大小(),但存储在main()中完成。

public static void main(String[] args) { 
    long num = 0; 
    int maxSize = 0; 

    long start = new Date().getTime(); 
    for (long i = 1; i <= TARGET; i++) { 
     int size = size(i); 
     if (size >= maxSize) { 
      maxSize = size; 
      num = i; 
     } 
     cache.put(i, size); 
    } 

    long computeTime = new Date().getTime() - start; 
    System.out.println(String.format("maxSize: %4d on initial starting number %6d", maxSize, num)); 
    System.out.println("compute time in milliseconds: " + computeTime); 
} 

private static int size(long i) { 
    if (i == 1l) { 
     return 1; 
    } 

    if (cache.containsKey(i)) { 
     return cache.get(i); 
    } 

    return size(process(i)) + 1; 
} 

注意,通过去除cache.put()的大小()调用,它不缓存每个计算的大小,但它也避免了重新缓存先前计算的大小。这不会影响hashmap操作,但像akf指出的那样,它可以避免自动装箱/拆箱操作,这是堆杀手来自的地方。我还在size()中尝试了一个“if(!containsKey(i)){cache.put()etc”,但不幸的是内存不足。

1

size(long i)这两个暗示之间的一个显着区别在于您创建的对象的数量。

在第一个实现中,没有创建Objects。在第二步中,您正在进行大量的自动装箱,为每个缓存访问创建一个新的Long,并在每次修改时添加新的Long和新的Integer

这将解释内存使用量的增加,但不是缺少OutOfMemoryError。增加堆允许它为我完成。

this Sun aritcle

The performance ... is likely to be poor, as it boxes or unboxes on every get or set operation. It is plenty fast enough for occasional use, but it would be folly to use it in a performance critical inner loop.

+0

增加的内存使用量可能是导致OOM的原因 - 我猜测快速对象生成会在垃圾回收器开始释放未使用的对象之前咀嚼内存。 – weiji 2009-08-27 23:20:06

相关问题