Comportamento indefinito in Java

14

Stavo leggendo questa domanda su SO che discute alcuni comportamenti non definiti comuni in C ++, e mi chiedevo: anche Java ha un comportamento indefinito?

Se è così, allora quali sono alcune cause comuni di comportamento non definito in Java?

Se no, allora quali caratteristiche di Java lo rendono libero da tali comportamenti e perché non sono state implementate le ultime versioni di C e C ++ con queste proprietà?

    
posta Eight 22.06.2012 - 10:47
fonte

4 risposte

17

In Java, puoi considerare il comportamento del programma non correttamente sincronizzato non definito.

Java 7 JLS utilizza la parola "undefined" una volta, in 17.4.8. Esecuzioni e requisiti di causalità :

We use f|d to denote the function given by restricting the domain of f to d. For all x in d, f|d(x) = f(x), and for all x not in d, f|d(x) is undefined...

La documentazione dell'API Java specifica alcuni casi in cui i risultati non sono definiti - ad esempio, in (deprecato) costruttore Date (int anno, int mese, int giorno) :

The result is undefined if a given argument is out of bounds...

Javadocs per ExecutorService. invokeAll (Collection) state:

The results of this method are undefined if the given collection is modified while this operation is in progress...

Il tipo meno formale di comportamento "indefinito" può essere trovato ad esempio in ConcurrentModificationException , dove i documenti API utilizzano il termine "miglior sforzo":

Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness...

Appendice

Una delle domande commenti fa riferimento a un articolo di Eric Lippert che fornisce un'introduzione utile all'argomento: Comportamento definito dall'implementazione .

Raccomando questo articolo per il ragionamento agnostico della lingua, anche se vale la pena tenere a mente che l'autore ha come target C #, non Java.

Traditionally we say that a programming language idiom has undefined behaviour if use of that idiom can have any effect whatsoever; it can work the way you expect it to or it can erase your hard disk or crash your machine. Moreover, the compiler author is under no obligation to warn you about the undefined behaviour. (And in fact, there are some languages in which programs that use "undefined behaviour" idioms are permitted by the language specification to crash the compiler!)...

By contrast, an idiom that has implementation-defined behaviour is behaviour where the compiler author has several choices about how to implement the feature, and must choose one. As the name implies, implementation-defined behaviour is at least defined. For example, C# permits an implementation to throw an exception or produce a value when an integer division overflows, but the implementation must pick one. It cannot erase your hard disk...

What are some of the factors that lead a language design committee to leave certain language idioms as undefined or implementation-defined behaviours?

The first major factor is: are there two existing implementations of the language in the marketplace that disagree on the behaviour of a particular program? ...

The next major factor is: does the feature naturally present many different possibilities for implementation, some of which are clearly better than others? ...

A third factor is: is the feature so complex that a detailed breakdown of its exact behaviour would be difficult or expensive to specify? ...

A fourth factor is: does the feature impose a high burden on the compiler to analyze? ...

A fifth factor is: does the feature impose a high burden on the runtime environment? ...

A sixth factor is: does making the behaviour defined preclude some major optimization? ...

Those are just a few factors that come to mind; there are of course many, many other factors that language design committees debate before making a feature "implementation defined" or "undefined".

Above è solo una copertura molto breve; l'articolo completo contiene spiegazioni ed esempi per i punti menzionati in questo estratto; è molto che vale la pena leggere. Ad esempio, i dettagli forniti per il "sesto fattore" possono dare una comprensione della motivazione per molte affermazioni nel modello di memoria Java ( JSR 133 ), che aiuta a comprendere il motivo per cui alcune ottimizzazioni sono consentite, portando a comportamenti indefiniti mentre altri sono proibiti, portando a limitazioni come succedere-prima e requisiti di causalità .

None of the article materials is particularly new to me but I'll be damned if I ever seen it presented in such an elegant, consise and understandable way. Amazing.

    
risposta data 22.06.2012 - 11:28
fonte
8

In cima alla mia testa, non penso che ci sia un comportamento indefinito in Java, almeno non nello stesso senso del C ++.

La ragione di ciò è che dietro a Java c'è una filosofia diversa rispetto a C ++. L'obiettivo principale della progettazione di Java era quello di consentire ai programmi di funzionare invariati attraverso le piattaforme, quindi la specifica definisce tutto in modo molto esplicito.

Al contrario, l'obiettivo principale della progettazione di C e C ++ è l'efficienza: non dovrebbero esserci caratteristiche (compresa l'indipendenza dalla piattaforma) che costano le prestazioni anche se non ne hai bisogno. A tal fine, le specifiche deliberatamente non definiscono alcuni comportamenti perché la loro definizione causerebbe un lavoro extra su alcune piattaforme e quindi ridurrebbe le prestazioni anche per le persone che scrivono programmi specifici per una piattaforma e sono consapevoli di tutte le sue idiosincrasie.

C'è persino un esempio in cui Java è stato costretto a introdurre retroattivamente una forma limitata di comportamento non definito esattamente per questo motivo: il strictfp la parola chiave è stata introdotta in Java 1.2 per consentire ai calcoli in virgola mobile di deviare dal seguire esattamente lo standard IEEE 754 come richiesto in precedenza, poiché ciò richiedeva un lavoro extra e rendeva più lenti tutti i calcoli in virgola mobile su alcune CPU comuni, mentre produceva effettivamente risultati peggiori in alcuni casi.

    
risposta data 22.06.2012 - 11:17
fonte
3

Java prova a stento a sterminare comportamenti non definiti, proprio a causa delle lezioni delle lingue precedenti. Ad esempio, le variabili a livello di classe vengono automaticamente inizializzate; le variabili locali non sono autoinizializzate per motivi di prestazioni, ma esiste un'analisi sofisticata del flusso di dati per impedire a chiunque di scrivere un programma che sia in grado di rilevarlo. I riferimenti non sono puntatori, quindi i riferimenti non validi non possono esistere e il dereferenziazione null causa un'eccezione specifica.

Ovviamente rimangono alcuni comportamenti che non sono completamente specificati, e puoi scrivere programmi inaffidabili se ritieni che lo siano. Ad esempio, se esegui un'iterazione su un Set normale (non ordinato), la lingua garantisce che vedrai ciascun elemento esattamente una volta, ma non in quale ordine li vedrai. L'ordine potrebbe essere lo stesso nelle esecuzioni successive o potrebbe cambiare; o potrebbe rimanere lo stesso se non si verificano altre allocazioni, o finché non si aggiorna il proprio JDK, ecc. È praticamente impossibile eliminare tutti tali effetti; per esempio, dovresti ordinare o randomizzare in modo esplicito tutte le operazioni di raccolta, e questo semplicemente non vale il piccolo valore aggiuntivo non-definito.

    
risposta data 22.06.2012 - 11:12
fonte
1

Devi capire il "comportamento indefinito" e la sua origine.

Comportamento indefinito indica un comportamento che non è definito dagli standard. C / C ++ ha troppe implementazioni del compilatore e funzionalità aggiuntive. Queste funzionalità aggiuntive hanno legato il codice al compilatore. Questo perché non c'era uno sviluppo del linguaggio centralizzato. Quindi alcune delle funzionalità avanzate di alcuni compilatori sono diventate "comportamenti indefiniti".

Mentre in Java le specifiche del linguaggio sono controllate da Sun-Oracle e nessun altro sta cercando di rendere specifiche e quindi nessun comportamento indefinito.

Modificato in particolare per rispondere alla domanda

  1. Java è privo di comportamenti non definiti perché gli standard sono stati creati prima dei compilatori
  2. I compilatori C / C ++ moderni hanno più o meno standardizzato le implementazioni, ma le funzionalità implementate prima della standardizzazione rimangono ancora etichettate come "comportamento indefinito" perché ISO ha mantenuto la mamma su questi aspetti.
risposta data 22.06.2012 - 12:55
fonte

Leggi altre domande sui tag