2009-08-27 76 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

相關問題