2014-06-07 15 views
14

Ricevo un messaggio di errore dal compilatore Java che non capisco. Ho testato il mio codice su OSX 10.6, 10.9 e Ubuntu 14.04, con Java 6 e 7. Quando corro con il debugger Eclipse o dall'interprete (usando l'opzione -Xint), tutto gira bene. In caso contrario, ottengo i seguenti messaggi:Errore irreversibile Java SIGSEGV

Java 1.6:

Invalid memory access of location 0x8 rip=0x1024e9660 

Java 1.7:

# 
# A fatal error has been detected by the Java Runtime Environment: 
# 
# SIGSEGV (0xb) at pc=0x000000010f7a8262, pid=20344, tid=18179 
# 
# JRE version: Java(TM) SE Runtime Environment (7.0_60-b19) (build 1.7.0_60-b19) 
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.60-b09 mixed mode bsd-amd64 compressed oops) 
# Problematic frame: 
# V [libjvm.dylib+0x3a8262] PhaseIdealLoop::idom_no_update(Node*) const+0x12 
# 
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again 
# 
# If you would like to submit a bug report, please visit: 
# http://bugreport.sun.com/bugreport/crash.jsp 
# 

C'è di più output di errore per Java 7 (che viene salvato in un file), ma purtroppo ho non può adattarsi nel limite di caratteri di questo post. A volte ho bisogno di eseguire il mio codice un paio di volte per l'errore, ma appare più spesso.

Il mio caso di test comporta l'archiviazione in cache di alcuni calcoli in scala logaritmica. Specificamente, dato log (X), log (Y), ..., ho una piccola classe che calcola log (X + Y + ...). E poi memorizzo il risultato in una HashMap.

Stranamente, la modifica di alcuni indici di loop sembra far sì che il problema scompaia. In particolare, se sostituisco

for (int z = 1; z < x+1; z++) { 
    double logSummand = Math.log(z + x + y); 
    toReturn.addLogSummand(logSummand); 
} 

con

for (int z = 0; z < x; z++) { 
    double logSummand = Math.log(1 + z + x + y); 
    toReturn.addLogSummand(logSummand); 
} 

allora non ho ricevuto il messaggio di errore e il programma funziona bene.

Il mio esempio minimo è qui sotto:

import java.util.Arrays; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
public class TestLogSum { 
    public static void main(String[] args) { 

     for (int i = 0; i < 6; i++) { 
      for (int n = 2; n < 30; n++) { 
       for (int j = 1; j <= n; j++) { 
        for (int k = 1; k <= j; k++) { 
         System.out.println(computeSum(k, j));      
        } 
       } 
      } 
     } 
    } 

    private static Map<List<Integer>, Double> cache = new HashMap<List<Integer>, Double>(); 
    public static double computeSum(int x, int y) {  
     List<Integer> key = Arrays.asList(new Integer[] {x, y}); 

     if (!cache.containsKey(key)) { 

      // explicitly creating/updating a double[] array, instead of using the LogSumArray wrapper object, will prevent the error 
      LogSumArray toReturn = new LogSumArray(x); 

      // changing loop indices will prevent the error 
      // in particular, for(z=0; z<x-1; z++), and then using z+1 in place of z, will not produce error 
//   for (int z = 0; z < x; z++) { 
//    double logSummand = Math.log(1 + z + x + y); 
      for (int z = 1; z < x+1; z++) { 
       double logSummand = Math.log(z + x + y); 
       toReturn.addLogSummand(logSummand); 
      } 

      // returning the value here without cacheing it will prevent the segfault 
      cache.put(key, toReturn.retrieveLogSum()); 
     } 
     return cache.get(key); 
    } 

    /* 
    * Given a bunch of logarithms log(X),log(Y),log(Z),... 
    * This class is used to compute the log of the sum, log(X+Y+Z+...) 
    */ 
    private static class LogSumArray {  
     private double[] logSummandArray; 
     private int currSize; 

     private double maxLogSummand; 

     public LogSumArray(int maxEntries) { 
      this.logSummandArray = new double[maxEntries]; 

      this.currSize = 0; 
      this.maxLogSummand = Double.NEGATIVE_INFINITY; 
     } 

     public void addLogSummand(double logSummand) { 
      logSummandArray[currSize] = logSummand; 
      currSize++; 
      // removing this line will prevent the error 
      maxLogSummand = Math.max(maxLogSummand, logSummand); 
     } 

     public double retrieveLogSum() { 
      if (maxLogSummand == Double.NEGATIVE_INFINITY) return Double.NEGATIVE_INFINITY; 

      assert currSize <= logSummandArray.length; 

      double factorSum = 0; 
      for (int i = 0; i < currSize; i++) { 
       factorSum += Math.exp(logSummandArray[i] - maxLogSummand); 
      } 

      return Math.log(factorSum) + maxLogSummand; 
     } 
    } 
} 
+0

Questo probabilmente non è un bug nel programma (i semplici programmi Java non dovrebbero mai causare un errore di segmentazione). Potrebbe essere un bug nel tuo hardware o nella tua implementazione di jvm. Prova un controllo completo della memoria e prova una macchina diversa. – Fabian

+0

Ho sempre riscontrato questo errore esatto quando scrivevo codice nativo (C, C++, ecc.) Tramite JNI. Stai usando il codice nativo (non mostrato nella domanda) o è davvero tutto Java? Come già sottolineato, Java puro non dovrebbe mai causare un segfault. – Radiodef

+3

Dopo aver eseguito il codice, ottengo anche un errore di segmentazione con "java version" 1.7.0_55 " OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-2) OpenJDK 64-Bit Server VM (build 24.51 -b03, modalità mista) ". – Fabian

risposta

15

Così, dopo aver letto i commenti, sembra che questo è un bug nella JVM che deve essere riferito a Oracle. Quindi, sono andato avanti e ho presentato una segnalazione di bug a Oracle. Pubblicherò aggiornamenti quando avrò notizie da loro.

Grazie a tutti coloro che hanno provato il codice e hanno trovato che si rompono anche sulle macchine.

Se c'è qualcuno con la capacità/voglia di capire quale codice nel compilatore sta causando questo errore, sarebbe fantastico sentire su di esso :)

UPDATE: Qualcuno da Oracle ha risposto ieri, ha detto che ha preparato una correzione per il bug e ha anche chiesto di includere il mio codice come test di regressione :) Non ha spiegato quale fosse il problema, oltre a dire che era nel JIT HotSpot, ma mi ha mandato un link con il modifiche apportate, nel caso qualcuno fosse interessato: http://cr.openjdk.java.net/~kvn/8046516/webrev/

Problemi correlati