Newsgroup: it.comp.java
Titolo: Metodo println e funzionamento della JVM

Salve,
Una bella domandina tecnica:
Da  quello  che  ho  capito  per portare Java su un altra piattaforma, c'e'
bisogno  di  convertire  la  JavaVirtualMachine, ma...tutto qui?  Mi spiego
meglio con un esempio:
Prendendo   un  classico  noioso  System.out.println("Hello,  World!"),  il
compilatore  traduce  il  sorgente  in  bytecode  dove c'e' una chiamata al
metodo println di java.io.PrintStream.
*1* (Object->OutputStream->FilterOutputStream->PrintStream)
La  JVM esegue il metodo println....ma, qui viene il ma:  come fa il metodo
println  in  semplice  bytecode  ad implementare una funzione print su ogni
piattaforma.
Cioe' ogni sistema operativo ha il suo modo di scrivere sulla console, e il
bytecode di println come fa a scrivere allora?
C'e' bisogno di  un metodo nativo o no?
Eppure io ho visto il dissasemblato di tutte le classi in ordine gerarchico
fino alla PrintStream (vedi *1*) e non e' presente nessun metodo nativo per
scrivere!!!
Ma allora come diavolo fa il metodo println a scrivere sulla console di ogni
piattaforma???
Spero di essermi spiegato.
Grazie a chiunque mi aiutera' a capire.
-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp
   Create Your Own Free Member Forum

...
Una delle caratteristiche del linguaggio Java è quella di essere machine
independent nel senso che un programma scritto in Java può essere eseguito
su qualsiasi  computer (con qualsiasi sistema operativo) .. che supporti
Java.
Un programma Java, ad esempio HelloWorld.java è un semplice file ASCII
scritto seguendo la sintassi Java. Con la compilazione viene creato il file
HelloWorld.class che è il cosiddetto bytecode. HelloWorld.class è machine
independent.
Questo discorso è facilmente comprensibile pensando ad internet. Io creo il
mio HelloWorld.java, lo compilo ottenendo HelloWorld.class e lo richiamo
come applet su un file html. Un  utente (qualsiasi computer/SO abbia) quando
richiama il documento html esegue sul suo computer anche l'applet che ho
scritto io sul mio computer.
Come funziona il tutto?
Il bytecode (cioè HelloWorld.class) non è un file direttamente eseguibile ma
un insieme di istruzioni che vengono interpretate in ogni computer dalla
JVM.
In sintesi c'è una parte machine independent che è proprio il bytecode, ma
vi è anche una parte machine dependent che interpreta il bytecode in
relazione ad una macchina specifica.
Ancora un esempio. Se eseguo l'applet HelloWorld.class su Netscape/Linux o
su Explorer/Win95 sui due browser viene caricato lo stesso bytecode ma viene
interpretato da due JVM diverse (una è quella di Netscape su Linux ed una è
quella MSIE per Win95).
A questo punto è evidente come sia necessaria una parte dipendente dalla
macchina anche per eseguire un applet semplice come il nostro
HelloWorld.class infatti anche semplici scritture sullo standard output con
due SO differenti vengono effettuate in modi completamente diversi e spesso
incompatibili ma la caratteristica di Java è proprio quella di slegare il
programmatore da questi dettagli e permettere la scrittura di software
"universale".
Concludendo non è che si debba convertire la JVM, ma ogni macchina deve
avere la sua JVM per poter interpretare il bytecode.
Spero di esserti stato utile e d di non essere stato troppo lungo e noiso.
-----
  Alessandro Caproni
 
cap...@cifra.it
 
cap...@cli.di.unipi.it
    home page: 
http://www.cli.di.unipi.it/~caproni/intro.html

 Spero di esserti stato utile e d di non essere stato troppo lungo e noiso.
Ancora no! Spero di essermi spiegato meglio ora. :)
Aspetto tue nuove! :)


 piattaforma???
Mai provato a dare un'occhiata al sorgente di java.io.PrintStream?
-- 
"Despite all my rage I'm still just a rat in a cage"

 Mai provato a dare un'occhiata al sorgente di java.io.PrintStream?
Avevo gia' detto nel messaggio originale che ho visto il disassemblato
di tutta la gerarchia fino a java.io.PrintStream.
Comunque questo basta? :)
(l'output non e' generato da javap ma da un mio programmino C, comunque
penso si capisca) :
»»» Method 19 of 26 : Public Synchronised void println(java/lang/String) «««
Max Stack Depth : 2
Local Variables : 2
Code Len : 00000012
### BYTECODE ###
----------------
0: aload_0
1: aload_1
2: invokevirtual java/io/PrintStream.#void print(java/lang/String)#
5: aload_0
6: bipush 10
8: invokevirtual java/io/PrintStream.#void write(int)#
11: return
--- Non sto a riportare tutte le successive chiamate (vedi riga 2 e 8), che a
sua volta chiamano altri metodi, perche' il msg diventerebbe bello lungo.
Il mio dilemma e' che in tutta sta trafila di chiamate a metodi per scrivere
qualcosa sulla console, non ho trovato nessun metodo nativo che alla fine si
occupa di stampare. Quello che non capisco e' che se non c'e' un metodo nativo
alla fine il semplice bytecode come fa a stampare su qualsiasi piattaforma??
Credo non sia molto facile da spiegare, ma spero di nuovo di essermi spiegato
un po' meglio.
Ciao.


 un po' meglio.
Dunque, i metodi nativi che cerchi sono in java.io.FileOutputStream. Ti
riporto
qui parte del codice di java.io.FileOutputStream, ottenuto con Jad
(
http://web.unicom.com.cy/~kpd/jad.html
). Se vuoi vedere il codice
completo,
ti conviene scaricarti Jad, che IMHO funziona perfettamente. 
// Decompiled by Jad v1.5.5.3. Copyright 1997-98 Pavel Kouznetsov.
// Jad home page:      
http://web.unicom.com.cy/~kpd/jad.html
// Decompiler options: packimports(3)
// Source File Name:   FileOutputStream.java
package 
java.io
;
// Referenced classes of package 
java.io
:
//            OutputStream, File, FileDescriptor, FileNotFoundException,
//            IOException
public class FileOutputStream extends OutputStream
{
[...]
    private native void open(String s)
        throws IOException;
    private native void openAppend(String s)
        throws IOException;
    public native void write(int i)
        throws IOException;
    private native void writeBytes(byte abyte0[], int i, int j)
        throws IOException;
    public void write(byte abyte0[])
        throws IOException
    {
        writeBytes(abyte0, 0, abyte0.length);
    }
}
I metodi write sono quelli che vengono invocati da print e println in
java.io.PrintStream.
Per arrivare a FileOutputStream, ho cercato prima di risalire attraverso
java.io.PrintStream, ma arrivato a java.io.OutputStream ho trovato che
il metodo abstract write(int i). Allora ho disassemblato
java.lang.System,
dove nell'inizializzazione trovi:
        FileInputStream fileinputstream = new
FileInputStream(FileDescriptor.in);
        FileOutputStream fileoutputstream = new
FileOutputStream(FileDescriptor.out);
        FileOutputStream fileoutputstream1 = new
FileOutputStream(FileDescriptor.err);
        setIn0(new BufferedInputStream(fileinputstream));
        setOut0(new PrintStream(new
BufferedOutputStream(fileoutputstream, 128), true));
        setErr0(new PrintStream(new
BufferedOutputStream(fileoutputstream1, 128), true));
Non ho disassemblato BufferedOutputStream, ma immagino che non faccia
altro che
utilizzare i metodi write di FileOutputStream.
Nota che setIn0, setOuto0, setErr0 sono metodi nativi di System.
Spero di aver risolto i tuoi dubbi.
Saluti
	Alberto
PS Domandina: Disassemblando il file classes.zip vado contro qualche
parte della
   licenza del JDK?

un po' meglio.
Secondo me la tua domanda era chiara sin dall'inizio e concordo con la prima
risposta che hai ricevuto, quella di Alessandro Caproni. Poi hai ribadito la
tua domanda: forse non ho capito bene il tuo problema!
Ci risentiamo!
Pino.

    licenza del JDK?
Boh, se non lo distribuisci ai quattro venti credo di no! :)
Grazie ancora!


 Pino.



Gia fatto! :-)
Grazie a voi tutti.
Alla prossima.
Pino.

...
Ok, proverò ad essere più chiaro.
Il codice prodotto dal compilatore java è un bytecode il quale può essere
eseguito su qualsiasi macchina (è il bello di java).
Il bytecode NON è assembler, se lo fosse lo stesso programma non potrebbe
girare su due macchine aventi CPU diverse (banale).
Seconda considerazione. Supponiamo di scrivere su un file. Se il programma
può essere eseguito su qualsiasi macchina, deve poter scrivere su qualsiasi
file system (ad esempio FAT16 e FAT32, ma anche i diversi file system di
Linux, NTFS di NT4, e così via..).
Se esistesse quello che tu chiami metodo nativo per scrivere su un file,
dovrebbe contemplare tutti i casi.
Java invece funziona diversamente (e più efficientemente).
Il bytecode conterrà ad esempio l'istruzione wrt(f,'a'); cioè scrivi il
carattere 'a' nel file f.
Come avviene la scrittura?
Ricordiamo che java non è compilato, ma interpretato. L'interprete è
specifico per la macchina su cui viene eseguito il bytecode (l'interpete
linux sarà diverso da quello di NT o di W95); quando trova l'istruzione wrt,
l'interprete la traduce nella sequenza di istruzioni assembler, specifiche
per la macchina su cui gira ed il file system che adotta, per ottenere la
scrittura di 'a' sul file f.
Qui è il bello: non esiste un metodo nativo, ma è l'interprete che scende
nel dettaglio della macchina perchè è l'unico che la conosce (il bytecode,
ripeto, vale per tutte le macchine e per nessuna in particolare: nulla sa
dell'hardware, ma per scrivere su un file l'hardware bisogna conoscerlo
bene; aggiungerei anzi una cosa: l'interprete non scrive nemmeno
direttamente sul file, ma delegherà il SO a farlo chiamando la funzione di
sistema apposita).
Sempre per essere lungo..
Ricordi il veccio basic dei PC o dei commodore?
Uno scriveva il programma basic ed il computer lo eseguiva. Ma come? Ogni
riga veniva interpretata, ne veniva controllata la sintassi, veniva tradotta
in linguaggio macchina ed eseguita. Poi si passava alla riga successiva.
Con questo sistema, lo stesso programma poteva essere eseguito su due
computer diversi, bastava cambiare l'interprete in modo che producesse
istruzioni diverse a seconda del computer su cui doveva girare il
programma.Ti chiedo, esisteva un metodo nativo per la print ? Direi di no.
Esisteva un interprete, distinto per ogni computer.
Non vorrei che tu stessi solo mischiando le carte.
Credo che il discorso di come venga eseguito un programma java sia
abbastanza chiaro. Quello che cerchi tu, forse, è l'ultimo metodo nella
gerarchia degli oggetti con cui viene tradotta ad esempio la scrittura su
console prima di essere tradotta in bytecode.
Se così fosse sei ad un livello più alto del discorso che sto portando
avanti.
Se così non fosse o non ti è chiara quest'ultima affermazione, comunicamelo.
Ciao


 questo metodo nativo??
Nella classe FileOutputStream:
public native void write(int b) throws IOException;
Ovviamente i metodi nativi vanno cercati nelle classi che implementano
degli stream "concreti", come appunto FileOutputStream e 
FileInputStream.
Un'ultima cosa: nel JDK sono inclusi tutti i sorgenti dei package 
java.*... la prossima volta usa quelli invece di disassemblare le classi!


 Pino.
Ciao e granzie anche a te!.


 console prima di essere tradotta in bytecode.
In un certo senso...cercavo l'ultimo metodo NATIVO che scrive effettivamente
sulla console!
Grazie per il tuo aiuto.
Alla prossima,
Ciao.


 java.*... la prossima volta usa quelli invece di disassemblare le classi!
Vabbe' l'avevo fatto giusto per sperimentare il mio programillo C che
dissasembla le classi.
Comunque, mi pare che nel src.zip non ci sono tutti i sorgenti o no!?
(i bean, le classi  sun.*)


 (i bean, le classi  sun.*)
Ci sono tutti i sorgenti della libreria standard, cioe' tutti i package
che cominciano per "java" (
java.io
, 
java.net
 etc. etc.)
I package sun.* non sono documentati e non ci sono i sorgenti perche'
la Sun non vuole che vengano usati - anche se ci sono parecchie cose
utili & interessanti.


 utili & interessanti.>
Dandogli una spulciata ho notato che parecchie classi standard richiamano
le classi di sun.*, quindi perche' non metterle dentro java.* ?? Boh! :))


le classi di sun.*, quindi perche' non metterle dentro java.* ?? Boh! :))
Perché le classi in sun.* sono un "dettaglio di implementazione",
che Sun si riserva di cambiare anche drasticamente a un qualsiasi
release, anche minore, quindi nessun utente di Java deve fare il
minimo affidamento su quel che trova implementato in sun.* in un
qualche particolare release.  Le classi in java.* invece sono parte
dello standard Java, e quindi puoi fare affidamento su di esse (su
come appaiono "da fuori", in termini di membri pubblici e protetti,
solamente; non sulla loro _implementazione_!).
Alex