Java copia i parametri del metodo nello stack frame del metodo chiamato?

2

Questo fa parte della risposta sullo stack e l'heap in Java:

So, why have the stack or the heap at all? For things that leave scope, the stack can be expensive. Consider the code:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

The parameters are part of the stack too. In the situation that you don't have a heap, you would be passing the full set of values on the stack. This is fine for "foo" and small strings... but what would happen if someone put a huge XML file in that string. Each call would copy the entire huge string onto the stack - and that would be quite wasteful.

Questo è ciò che ho letto in Java Java Virtual Application Specification SE 8 Edition (Capitolo 2.5.2 Stack di Java Virtual Machine):

It [Java Virtual Machine stack] holds local variables and partial results, and plays a part in method invocation and return.

Quello che so è che quando passo un riferimento come parametro a un metodo, viene creata una variabile locale all'interno del metodo. La variabile locale contiene lo stesso riferimento della variabile passata. Ad esempio, qualcosa di simile

void foo(Bar bar) {
    // Do something with bar
}

diventa qualcosa di simile a questo:

void foo(Bar bar) {
    Bar localBar = bar;
    // Do something with localBar
}

Quindi la mia domanda è:

Java copia i parametri del metodo nello stack frame del metodo chiamato? Da la risposta a cui mi riferisco all'inizio della pagina, capisco che siano copiati, ed è una copia profonda. Quasi sicuro, mi sbaglio.

    
posta Maksim Dmitriev 28.05.2017 - 13:43
fonte

2 risposte

6

In Java, lo stack di distinzione rispetto all'heap non è molto significativo, perché Java non ti dà una scelta dove i valori sono vivi. Concettualmente, tutti gli oggetti vivono da qualche parte nell'heap, ma in Java un oggetto non è la stessa cosa di una variabile. Le variabili locali ei parametri del metodo usano lo spazio sullo stack, ma le variabili non trattengono mai direttamente un oggetto. Le variabili contengono valori primitivi come int o riferimenti agli oggetti.

In Java, l'assegnazione delle variabili esegue sempre una copia superficiale: per i valori primitivi, il valore viene copiato. Per gli oggetti, il riferimento è copiato. L'oggetto a cui si fa riferimento non viene copiato: il riferimento originale e il riferimento copiato si riferiscono entrambi allo stesso oggetto.

Le chiamate di metodo funzionano molto come l'assegnazione delle variabili ai parametri. Quando viene chiamato un metodo, puoi pensare che uno stack frame con i parametri del metodo sia allocato nello stack e che quindi assegniamo i valori dell'argomento a ogni variabile parametro.

Poiché viene eseguita una copia superficiale di tutti i valori degli argomenti, non è possibile modificare le variabili esterne assegnando a un parametro all'interno di un metodo:

void outer() {
  int i = 4;
  inner(i);
  // i will always be 4 at this point
}

void inner(int j) {
  j = 7;  // this assignment only affects the *local* j
}

Ma se passi un oggetto non c'è una copia profonda, quindi le modifiche all'oggetto sono visibili al di fuori del metodo:

void outer() {
  ArrayList<String> xs = new ArrayList<>();
  inner(xs);
  // xs has been modified at this point, and contains "foo"
}

void inner(ArrayList<String> ys) {
  ys.add("foo");
}

La parte dalla risposta che hai citato non spiega come funziona Java, ma perché è desiderabile non fare copie profonde per ogni chiamata di metodo.

Il comportamento delle chiamate al metodo Java è molto simile a C e C ++: tutto è passato per valore . La differenza è che gli oggetti Java non sono un oggetto nel senso C e C ++, ma si comportano come un puntatore all'oggetto reale. Per le assegnazioni di variabili e le chiamate al metodo, quel puntatore viene quindi passato per valore.

    
risposta data 28.05.2017 - 14:13
fonte
0

In Java, hai oggetti (tutti che ereditano da java.lang.Object , heap-allocati) e tipi primitivi ( bool , char , tipi interi, float , double , nonché puntatori agli oggetti (chiamati riferimenti per scopi di marketing)).

Le variabili sono di tipo primitivo, quindi banalmente copiate e sempre passate per valore.

Gli oggetti non possono essere passati affatto, ma i puntatori a loro possono, in effetti, passarli per riferimento, non copiarli.

Le copie profonde di un tipo primitivo sono le stesse di quelle poco profonde.
Le copie di oggetti non avvengono affatto, a meno che tu non le faccia esplicitamente, e quindi è tua responsabilità fare / evitare la condivisione della struttura a seconda dei casi.

    
risposta data 28.05.2017 - 14:31
fonte

Leggi altre domande sui tag