JRE,JVM,JIT wytłumaczenie

0

Cześć. Uczę się nt. różnić pomiedzy JRE,JVM i JIT.

Jeżeli chodzi o JVM, to ogarnąłem mniej więcej jak to działa.

  1. javac (który się znajduje w JDK) konwertuje nasz napisy kod na tzw. "bytecode".

  2. jvm "łapie" ten bytocde i przechodząc przez te wszystkie komponenty czyli class loader, bytcode verifier, execution engine zamienia bytecode na kod maszynowy

i o ile się nie myle, to jit jest częścią execution engine. Nie potrafię ogarnąć jak to działa. Na stronkach pisza, że usprawnia on JVM, bo sam JVM byłby zbyt wolny ze wzgledu na konwertowanie całego bajtkodu na kod maszynowy. Ludzie piszą, że JIT (just-in-time) konwertuje w czasie run time kod, który jest najbardziej używany na kod maszynowy tak, aby cały kombajn JVM nie musiał konwertować wszystkiego samego sam.

Jak to działa, kiedy on występuje, czy wystepuje on rownolegle z "innymi czesciami" JVM i konwertuje w równoległym czasie najbardziej użytkowany kod?

Jeżeli chodzi o JRE, to na każdej stronie jest napisane, że;

"Actually JVM runs the program, and it uses the class libraries, and other" natomiast

"If you want to run any Java program, you need to have JRE installed in the system.", to co w koncu odpala ten program?
Tak wiem, że JRE = JVM+jakies tam biblioteki, ale to w takiem razie to skoro to i to odpala program to po co nam jest JRE?

Potrzebuje wytłumaczenia na chłopski rozum, najlepiej na przykładzie.

1

JIT jest częścią JVMki, a JVMka jest częścią JRE.

https://en.wikipedia.org/wiki/Java_virtual_machine#Java_Runtime_Environment

The Java Runtime Environment (JRE) released by Oracle is a freely available software distribution containing a stand-alone JVM (HotSpot), the Java standard library (Java Class Library), a configuration tool, and—until its discontinuation in JDK 9—a browser plug-in.

Kompilacja JIT (Just-in-time) jest jednym z wielu sposobów wykonywania kodu. Innymi są np kompilacja AOT (ahead-of-time jak np typowo w C/ C++) bądź interpretowanie. HotSpot czyli standardowa JVMka, startuje w trybie interpretera, a dla kodu, który jest najczęściej wykonywany (zabiera najwięcej czasu procesora) uruchamia kompilację JIT do zoptymalizowanego kodu maszynowego.

"If you want to run any Java program, you need to have JRE installed in the system.", to co w koncu odpala ten program?
Tak wiem, że JRE = JVM+jakies tam biblioteki, ale to w takiem razie to skoro to i to odpala program to po co nam jest JRE?

Te "jakieś tam biblioteki" to np klasy obsługujące pliki, wątki, GUI, sieć, itp itd w zasadzie bez nich nic użytecznego nie zrobisz. Część biblioteki standardowej jest w pewien sposób wbudowana w JVMa, bo wiele podstawowych klas wymaga bezpośredniego wsparcia od JVMa (zwłaszcza te klasy, które mają metody typu native).

0
Wibowit napisał(a):

JIT jest częścią JVMki, a JVMka jest częścią JRE.

Ja znam te wszystkie podstawy, co jest częścią czego.

Kompilacja JIT (Just-in-time) jest jednym z wielu sposobów wykonywania kodu. Innymi są np kompilacja AOT (ahead-of-time jak np typowo w C/ C++) bądź interpretowanie. HotSpot czyli standardowa JVMka, startuje w trybie interpretera, a dla kodu, który jest najczęściej wykonywany (zabiera najwięcej czasu procesora) uruchamia kompilację JIT do zoptymalizowanego kodu maszynowego.

Czy dobrze w takim razie opisałem wszystko jeżeli chodzi o poszczególne kroki JVM? JIT działa i konwertuje sobie najczęściej wykonywany kod, natomiast inny część zajmuje się całą resztą?

Szczerze powiedziawszy nic mi ten post nie dał, potrzebuje wyjaśnienia na chłopski rozum. Poza tym dalej nie rozumiem tego JRE i po mi to do końca jest, skoro JVM odpala program. :P

//EDIT
Widzę, że zedytowałeś post

Te "jakieś tam biblioteki" to np klasy obsługujące pliki, wątki, GUI, sieć, itp itd w zasadzie bez nich nic użytecznego nie zrobisz. Część biblioteki standardowej jest w pewien sposób wbudowana w JVMa, bo wiele podstawowych klas wymaga bezpośredniego wsparcia od JVMa (zwłaszcza te klasy, które mają metody typu native).

To w takim razie w jaki sposób JVM odpala program skoro nie ma tych plików, za co odpowiada to JRE? Niby daje środowisko, ale to w takim razie, JRE to jest komputer, który zawiera wszystkie sterowniki, a JVM to jest program .exe?

2

Ja znam te wszystkie podstawy, co jest częścią czego.

No chyba jednak nie, bo w kółko się pytasz.

Czy dobrze w takim razie opisałem wszystko jeżeli chodzi o poszczególne kroki JVM? JIT działa i konwertuje sobie najczęściej wykonywany kod, natomiast inny część zajmuje się całą resztą?

W standardowej Javie (OpenJDK/ OracleJDK) JIT odpalany jest dopiero po interpreterze, a więc najpierw zachodzi ładowanie bajtkodu ,weryfikowanie go, interpretowanie, a dopiero potem ewentualnie JITowanie do wydajnego kodu maszynowego.

To w takim razie w jaki sposób JVM odpala program skoro nie ma tych plików, za co odpowiada to JRE? Niby daje środowisko, ale to w takim razie, JRE to jest komputer, który zawiera wszystkie sterowniki, a JVM to jest program .exe?

JVM jest w stanie odpalić twój kod, bo ma namiary na bibliotekę standardową i ładuje ją razem z twoim programem. Jeśli wytniesz z biblioteki standardowej klasy typu java.lang.System to JVM się wywali z powodu braku niezbędnej klasy.

0
Wibowit napisał(a):

Ja znam te wszystkie podstawy, co jest częścią czego.

No chyba jednak nie, bo w kółko się pytasz.

Nie spytałem nigdzie co jest czego częścią :P

Czy dobrze w takim razie opisałem wszystko jeżeli chodzi o poszczególne kroki JVM? JIT działa i konwertuje sobie najczęściej wykonywany kod, natomiast inny część zajmuje się całą resztą?

W standardowej Javie (OpenJDK/ OracleJDK) JIT odpalany jest dopiero po interpreterze, a więc najpierw zachodzi ładowanie bajtkodu ,weryfikowanie go, interpretowanie, a dopiero potem ewentualnie JITowanie do wydajnego kodu maszynowego.

No i jesteśmy w momencie odpalenia JIT'a, który tylko CZĘŚĆ kodu przetwarza, a kiedy jest przetwarzana cała reszta?

To w takim razie w jaki sposób JVM odpala program skoro nie ma tych plików, za co odpowiada to JRE? Niby daje środowisko, ale to w takim razie, JRE to jest komputer, który zawiera wszystkie sterowniki, a JVM to jest program .exe?

JVM jest w stanie odpalić twój kod, bo ma namiary na bibliotekę standardową i ładuje ją razem z twoim programem. Jeśli wytniesz z biblioteki standardowej klasy typu java.lang.System to JVM się wywali z powodu braku niezbędnej klasy.

Czyli JVM konwertuje to wszystko do kodu maszynowego i częsciowo odpala program, ale nie może go odpalić całego więc potrzebuje całą resztę, która znajduje się JRE?

0

@jajko1233: kompilator Javy kompiluje Jave do bytecode, ale to nie jest kod maszynowy tylko taki język pośredni interpretowany.

2

No i jesteśmy w momencie odpalenia JIT'a, który tylko CZĘŚĆ kodu przetwarza, a kiedy jest przetwarzana cała reszta?

To nie jest tak, że JVMka nagle wyłącza interpreter i włącza JITa. Oba mechanizmy działają jednocześnie. Cały wykonywany bajtkod jest najpierw intepretowany. Dopiero gdy dany kawałek kodu był intepretowany wielokrotnie to jest podawany do JITa, który kompliuje go do wydajnego kodu maszynowego. JVMka dynamicznie przełącza się między zJITowanym kodem maszynowym, a własnym interpreterem.

Czyli JVM konwertuje to wszystko do kodu maszynowego i częsciowo odpala program, ale nie może go odpalić całego więc potrzebuje całą resztę, która znajduje się JRE?

JVM konwertuje do kodu maszynowego tylko kawałki. Ta kompilacja JIT ma wpływ tylko i wyłącznie na wydajność. Możesz ją całkowicie wyłączyć (przełącznik java -Xint). Biblioteka standardowa (czyli jeden z dwóch głównych składników JRE, oprócz JVMa) składa się z grubsza ze wszystkich klas z paczek java.* i javax.* Jaki program jesteś w stanie napisać nie korzystając z klas zawartych w paczkach java.*?

Ponadto: w HotSpocie (JVM zawarty w OpenJDK/ OracleJDK) JITowanie odbywa się w locie, równolegle do wykonywania (interpretowania) programu, a wyniki kompilacji siedzą sobie w RAMie i znikają po zakończeniu procesu.

0
Wibowit napisał(a):

No i jesteśmy w momencie odpalenia JIT'a, który tylko CZĘŚĆ kodu przetwarza, a kiedy jest przetwarzana cała reszta?

To nie jest tak, że JVMka nagle wyłącza interpreter i włącza JITa. Oba mechanizmy działają jednocześnie. Cały wykonywany bajtkod jest najpierw intepretowany. Dopiero gdy dany kawałek kodu był intepretowany wielokrotnie to jest podawany do JITa, który kompliuje go do wydajnego kodu maszynowego. JVMka dynamicznie przełącza się między zJITowanym kodem maszynowym, a własnym interpreterem.

A co z kodem, który nie był wielokrotnie używany, czyli tym który nie jest konwertowany przez JIT?

Czyli JVM konwertuje to wszystko do kodu maszynowego i częsciowo odpala program, ale nie może go odpalić całego więc potrzebuje całą resztę, która znajduje się JRE?

JVM konwertuje do kodu maszynowego tylko kawałki. Ta kompilacja JIT ma wpływ tylko i wyłącznie na wydajność. Możesz ją całkowicie wyłączyć (przełącznik java -Xint). Biblioteka standardowa (czyli jeden z dwóch głównych składników JRE, oprócz JVMa) składa się z grubsza ze wszystkich klas z paczek java.* i javax.* Jaki program jesteś w stanie napisać nie korzystając z klas zawartych w paczkach java.*?

To nie konwertuje całego kodu, który napisaliśmy (a raczej zamienionego kodu do bytecode) do kodu maszynowego? Przecież zadaniem JVM jest chyba załadowanie, sprawdzenie i przekonwertowanie kodu do kodu maszynowego, tak?

1

A co z kodem, który nie był wielokrotnie używany, czyli tym który nie jest konwertowany przez JIT?

No już napisałem kilka razy: bajtkod jest najpierw interpretowany, a dopiero gdy jest interpretowany wielokrotnie to jest JITowany. Proces odbywa się po kawałku, mniej więcej metoda po metodzie. W danym momencie są metody zJITowane i niezJITowane, więc jedne są w postaci kodu maszynowego, a drugie są dalej w postaci bajtkodu i muszą być nadal interpretowane.

To nie konwertuje całego kodu, który napisaliśmy (a raczej zamienionego kodu do bytecode) do kodu maszynowego? Przecież zadaniem JVM jest chyba załadowanie, sprawdzenie i przekonwertowanie kodu do kodu maszynowego, tak?

Zadaniem JVMa jest wykonanie programu zgodnie ze specyfikacją Javy. Specyfikacja Javy nie narzuca sposobu wykonywania bajtkodu. Bajtkod może być interpretowany, JITowany, AOTowany, a nawet wykonywany bezpośrednio przez procesor (jak w ARM Jazelle). Interpreter oraz JIT są wbudowane w standardową JVMkę. Kompilator AOT masz np tutaj: https://www.graalvm.org/docs/reference-manual/aot-compilation/ albo tutaj: https://www.excelsiorjet.com/ .

Ponadto jeśli chcesz być w 100% zgodnym z Javą to nie możesz skompilować w pewnym momencie całego bajtkodu do kodu maszynowego i wyłączyć interpreter oraz wszelkie kompilatory. Dzieje się tak dlatego iż w Javie w pełni legalnymi operacjami są dynamiczne (a więc w dowolnym momencie) ładowanie, generowanie bądź wzbogacanie bajtkodu. Możesz napisać program w Javie, w którym użytkownik może wybrać JARa (nazwijmy te JARy pluginami), a ty w swoim programie załadujesz tego JARa i np wywołasz z niego metodę main bez odpalania kolejnego procesu.

0
Wibowit napisał(a):

A co z kodem, który nie był wielokrotnie używany, czyli tym który nie jest konwertowany przez JIT?

No już napisałem kilka razy: bajtkod jest najpierw interpretowany, a dopiero gdy jest interpretowany wielokrotnie to jest JITowany. Proces odbywa się po kawałku, mniej więcej metoda po metodzie. W danym momencie są metody zJITowane i niezJITowane, więc jedne są w postaci kodu maszynowego, a drugie są dalej w postaci bajtkodu i muszą być nadal interpretowane.

To w takim razie czegoś nie zrozumiałem. Gdy ten bajtkod jest interpretowany kawałek po kawałku, metoda po metodzie to nie jest on wtedy zamieniany od razu na kod maszynowy?

To nie konwertuje całego kodu, który napisaliśmy (a raczej zamienionego kodu do bytecode) do kodu maszynowego? Przecież zadaniem JVM jest chyba załadowanie, sprawdzenie i przekonwertowanie kodu do kodu maszynowego, tak?

Zadaniem JVMa jest wykonanie programu zgodnie ze specyfikacją Javy. Specyfikacja Javy nie narzuca sposobu wykonywania bajtkodu. Bajtkod może być interpretowany, JITowany albo AOTowany. Interpreter oraz JIT są wbudowane w standardową JVMkę. Kompilator AOT masz np tutaj: https://www.graalvm.org/docs/reference-manual/aot-compilation/ albo tutaj: https://www.excelsiorjet.com/ .

Troche wydaje mi się odbieliśmy od tematu, poza tym jak już wspomniałem potrzebuje to na chłopski rozum mieć wytłmaczone, JVM a JRE. Dlaczego jedno bez drugiego by nie ruszyło, skoro jak sam już wspomniałeś > Zadaniem JVMa jest wykonanie programu
Rozumiem, że JRE ma jakieś ważne biblioteki, ale skoro JVM już sam w sobie wykonuje program, to dlaczego by nie przerzucić tych bibliotek do samego JVM i mieć tylko JDK i JVM?

1

To w takim razie czegoś nie zrozumiałem. Gdy ten bajtkod jest interpretowany kawałek po kawałku, metoda po metodzie to nie jest on wtedy zamieniany od razu na kod maszynowy?

Nie od razu. Przecież napisałem wiele razy, że dany kawałek bajtkodu musi być interpretowany wielokrotnie, by zajął się nim JIT. JITowanie to kosztowny proces, więc JVMka JITuje tylko to co się opłaca JITować. Dla przykładu: jeśli wykonanie metody 5x przez interpreter zajmuje 20ms, wykonanie 5x przed kod maszynowy zajmuje 5ms, ale skompilowanie JITem metody zajmuje 100ms, to opłaca się zostać przy samym interpreterze. Wykorzystanie samego interpretera da wynik czasowy równy 20ms podczas gdy JITowanie od razu + wykonanie zajmie 100ms + 5ms = 105ms, czyli ponad 5x więcej czasu.

Troche wydaje mi się odbieliśmy od tematu, poza tym jak już wspomniałem potrzebuje to na chłopski rozum mieć wytłmaczone, JVM a JRE. Dlaczego jedno bez drugiego by nie ruszyło, skoro jak sam już wspomniałeś > Zadaniem JVMa jest wykonanie programu
Rozumiem, że JRE ma jakieś ważne biblioteki, ale skoro JVM już sam w sobie wykonuje program, to dlaczego by nie przerzucić tych bibliotek do samego JVM i mieć tylko JDK i JVM?

To trochę jakby napisać: skoro do zjedzenia zupy potrzebujemy łyżki oraz talerza to czemu by nie zrobić jednego łyżkotalerza? Byłaby jedna rzecz mniej. Albo czemu samochód składa się podwozia i nadwozia? Niech składa się tylko z jednego wozia! :P
Po prostu masz równanie: JRE = JVM + biblioteka standardowa. Masz wiele implementacji JVM oraz wiele implementacji biblioteki standardowej (aczkolwiek kompatybilność między nimi nie zawsze istnieje). Dla przykładu na https://adoptopenjdk.net/ masz przełącznik Choose a JVM z opcjami HotSpot i OpenJ9. Masz do wyboru dwa JVMy, ale oba są połączone z tą samą biblioteką standardową. Są projekty, które implementują wyłącznie bibliotekę standardową, ale JVMa już nie, np: https://en.wikipedia.org/wiki/GNU_Classpath Google z https://en.wikipedia.org/wiki/Apache_Harmony wyciągnął tylko bibliotekę standardową, dołożył swoją JVMkę (Dalvik) i w ten sposób powstał Androidowy JRE. Teraz jednak pozbywa się biblioteki standardowej z Apache Harmony i zastępuje ją tą z OpenJDK. Jak widać implementacje biblioteki standardowej można mieszać na różne sposoby z implementacjami JVMki.

0
Wibowit napisał(a):

To w takim razie czegoś nie zrozumiałem. Gdy ten bajtkod jest interpretowany kawałek po kawałku, metoda po metodzie to nie jest on wtedy zamieniany od razu na kod maszynowy?

Nie od razu. Przecież napisałem wiele razy, że dany kawałek bajtkodu musi być interpretowany wielokrotnie, by zajął się nim JIT. JITowanie to kosztowny proces, więc JVMka JITuje tylko to co się opłaca JITować.

Nie wiem czy dużo mi brakuje do zrozumienia czy nie, ale potrzebuje tej jednej cały czas rzeczy by zrozumieć cały koncept. Czy to jest tak, że kod jest wiele razy interpretowany, JVM "widzi", która rzecz jest wielokrotnie używana i tą rzeczą zajmuje się JIT a reszta jest interpretowana jeszcze raz i zamieniania kawałek po kawałku na kod maszynowy?

Troche wydaje mi się odbieliśmy od tematu, poza tym jak już wspomniałem potrzebuje to na chłopski rozum mieć wytłmaczone, JVM a JRE. Dlaczego jedno bez drugiego by nie ruszyło, skoro jak sam już wspomniałeś > Zadaniem JVMa jest wykonanie programu
Rozumiem, że JRE ma jakieś ważne biblioteki, ale skoro JVM już sam w sobie wykonuje program, to dlaczego by nie przerzucić tych bibliotek do samego JVM i mieć tylko JDK i JVM?

To trochę jakby napisać: skoro do zjedzenia zupy potrzebujemy łyżki oraz talerza to czemu by nie zrobić jednego łyżkotalerza? Byłaby jedna rzecz mniej. Albo czemu samochód składa się podwozia i nadwozia? Niech składa się tylko z jednego wozia! :P
Po prostu masz równanie: JRE = JVM + biblioteka standardowa.

Eh, nie rozumiem tego dalej. JVM odpala program po uprzednim go zamienieniu na kod maszynowy, a JRE (tak wiem, że ma też JVM) ma jakieś tam w sobie biblioteki które sa potrzebne do odpalenia tego programu. Czyli JRE zajmuje się odpaleniem tego jako tak jakby całość.
JVM odpala program, ale potrzebuje bibliotek.

Tak to rozumiem.

1

Eh, nie rozumiem tego dalej. JVM odpala program po uprzednim go zamienieniu na kod maszynowy, a JRE (tak wiem, że ma też JVM) ma jakieś tam w sobie biblioteki które sa potrzebne do odpalenia tego programu. Czyli JRE zajmuje się odpaleniem tego jako tak jakby całość.
JVM odpala program, ale potrzebuje bibliotek.

Tak to rozumiem.

Masz wyobraźnię godną @maszynaz. JVM wykonuje program, a JRE wykonuje JVM? WTF?

Jeśli ktoś mówi, że JRE wykonuje program to ma na myśli, że JVM wykonuje program mając podpiętą bibliotekę standardową. Inaczej to nie zadziała. Bez biblioteki standardowej nie wypiszesz nawet tekstu na ekran, bo funkcjonalność wypisywania tekstu na ekran znajduje się w bibliotece standardowej.

JVM nie zamienia bajtkodu nad kod maszynowy przez odpaleniem go. Ile razy mam pisać, że bajtkod jest najpierw wykonywany przez interpreter, a dopiero w trakcie wykonywania programu JITowany po kawałku?

Wiesz w ogóle po co jest interpreter?

0
Wibowit napisał(a):

Eh, nie rozumiem tego dalej. JVM odpala program po uprzednim go zamienieniu na kod maszynowy, a JRE (tak wiem, że ma też JVM) ma jakieś tam w sobie biblioteki które sa potrzebne do odpalenia tego programu. Czyli JRE zajmuje się odpaleniem tego jako tak jakby całość.
JVM odpala program, ale potrzebuje bibliotek.

Tak to rozumiem.

Masz wyobraźnię godną @maszynaz. JVM wykonuje program, a JRE wykonuje JVM? WTF?

Jeśli ktoś mówi, że JRE wykonuje program to ma na myśli, że JVM wykonuje program mając podpiętą bibliotekę standardową. Inaczej to nie zadziała. Bez biblioteki standardowej nie wypiszesz nawet tekstu na ekran, bo funkcjonalność wypisywania tekstu na ekran znajduje się w bibliotece standardowej.

I w sumie teraz to zalapalem. Nie wiem co to za użytkownik ale w sumie to co napisałem, ze JVM wykonuje program, a JRE podpina biblioteki do JVM i odpala to jako całość to przełożyłes na słowa. Przynajmniej tak mi się wydaje :P

JVM nie zamienia bajtkodu nad kod maszynowy przez odpaleniem go. Ile razy mam pisać, że bajtkod jest najpierw wykonywany przez interpreter, a dopiero w trakcie wykonywania programu JITowany po kawałku?

Wiesz w ogóle po co jest interpreter?

Bajtkod jest wykonywany przez interpreter. Interpreter służy do konwertowania bajtkodu na kod maszynowy. Czyli program jest konwertowany na kod maszynowy. Później jest odpalany i jitowany, czyli zamieniany na kod maszynowy (chyba ze jit służy do czegoś innego i zle zrozumiałem przekaz w internecie) Dwa razy zamieniany bajtkod na kod maszynowy? Coś tu się nie klei.

1

Bajtkod jest wykonywany przez interpreter. Interpreter służy do konwertowania bajtkodu na kod maszynowy.

To jakiś schizofreniczny ten interpreter - nie wie na co się zdecydować. Interpreter oczywiście nie zamienia bajtkodu na kod maszynowy. Interpretowanie polega na wykonywaniu bajtkodów natychmiast, bez tłumaczenia ich na cokolwiek innego.

Dla przykładu tutaj masz język programowania, który nawet w źródłowej formie jest prostszy niż Javowy bajtkod: https://en.wikipedia.org/wiki/Brainfuck
Tutaj natomiast jest do niego bezpośredni (bez uprzedniego tłumaczenia na żadną inną postać pośrednią) interpreter: https://gist.githubusercontent.com/unnikked/cfad836abd9e4619a1b1/raw/e26adf1027a0696ec67fdac825b66656dacf8558/Brainfuck.java

import java.util.*;
public class Brainfuck {
    private Scanner sc = new Scanner(System.in);
    private final int LENGTH = 65535;
    private byte[] mem = new byte[LENGTH];
    private int dataPointer;

    public void interpret(String code) {
        int l = 0;
        for(int i = 0; i < code.length(); i++) {
            if(code.charAt(i) == '>') {
                dataPointer = (dataPointer == LENGTH-1) ? 0 : dataPointer + 1;
            } else if(code.charAt(i) == '<') {
                dataPointer = (dataPointer == 0) ? LENGTH-1 : dataPointer - 1;
            } else if(code.charAt(i) == '+') {
                mem[dataPointer]++;
            } else if(code.charAt(i) == '-') {
                mem[dataPointer]--;
            } else if(code.charAt(i) == '.') {
                System.out.print((char) mem[dataPointer]);
            } else if(code.charAt(i) == ',') {
                mem[dataPointer] = (byte) sc.next().charAt(0);
            } else if(code.charAt(i) == '[') {
                if(mem[dataPointer] == 0) {
                    i++;
                    while(l > 0 || code.charAt(i) != ']') {
                        if(code.charAt(i) == '[') l++;
                        if(code.charAt(i) == ']') l--;
                        i++;
                    }
                }
            } else if(code.charAt(i) == ']') {
                if(mem[dataPointer] != 0) {
                    i--;
                    while(l > 0 || code.charAt(i) != '[') {
                        if(code.charAt(i) == ']') l++;
                        if(code.charAt(i) == '[') l--;
                        i--;
                    }
                    i--;
                }
            }
        }
    }

    public static void main(String[] args) {
        new Brainfuck().interpret(args[0]);
    }
}
1

Polecam:

0
Wibowit napisał(a):

Bajtkod jest wykonywany przez interpreter. Interpreter służy do konwertowania bajtkodu na kod maszynowy.

To jakiś schizofreniczny ten interpreter - nie wie na co się zdecydować. Interpreter oczywiście nie zamienia bajtkodu na kod maszynowy. Interpretowanie polega na wykonywaniu bajtkodów natychmiast, bez tłumaczenia ich na cokolwiek innego.

A no, to wiele mi to wyjaśnia i temat jest coraz bardziej jasny (na jakiejś stronie w takim razie było źle napisane, bo bankowo było że przekłada on bajtkod na kod maszynowy). W każdym razie, to jeszcze raz. Mamy ten cały bajtkod w plikach .class, JVM interpetuje wielokrotnie te pliki i widzi które kawałki są powielane, te które są powielane to są zamieniane na kod maszynowy, aby "odpalenie" tych kawałków było wydajniejsze. To co nie jest kompilowane przez JIT jest dalej interpretowane, czyli włączane od razu z byka, bez żadnego zamieniania na kod maszynowy? Te kawałki, które opłaca się przełożyć na kod maszynowy opłacało się zamienić, reszty się nie opłacało.
Tak to działa?

0

Interpreter nie generuje żadnego kodu maszynowego.

JVM interpetuje wielokrotnie te pliki i widzi które kawałki są powielane, te które są powielane to są zamieniane na kod maszynowy, aby "odpalenie" tych kawałków było wydajniejsze.

Nie powielane, a wielokrotnie wykonywane. Powielanie oznacza raczej duplikację kodu (np metodą Kopiego-Pejsta).

0
Wibowit napisał(a):

Interpreter nie generuje żadnego kodu maszynowego.

No tak, nie generuje kodu maszynowego, tylko od razu odpala sobie bajtkod

Nie powielane, a wielokrotnie wykonywane. Powielanie oznacza raczej duplikację kodu (np metodą Kopiego-Pejsta).

No tak :D Dobrze to napisałem wszystko? Cały zamysł, etapy?

0
Wibowit napisał(a):

Interpreter nie generuje żadnego kodu maszynowego.

Nie?! To jak ten bytecode jest wykonywany, gdy nie jest 'zJITowany'?

0
Fedaykin napisał(a):
Wibowit napisał(a):

Interpreter nie generuje żadnego kodu maszynowego.

Nie?! To jak ten bytecode jest wykonywany, gdy nie jest 'zJITowany'?

Analogicznie do np interpretera Brainfucka kilka postów powyżej: https://4programmers.net/Forum/Java/322359-jrejvmjit_wytlumaczenie?p=1565591#id1565591

0

@Fedaykin jak w moim komentarzu wyżej do posta Wibowita -> kwestia definicjii "generowania". Interpreter nie tworzy żadnego nowego kodu maszynowego (co Wibowit uznaje za jedyną definicje "generowania"), tylko bierze sobie kawałki już przygotowane na etapie kompilacji tegoż interpretera i tłumaczy bajtkod na sekwencje złożoną z tych kawałków.
Z punktu widzenia CPU interpreter jak najbardziej generuje kod maszynowy do wykonania, z oczywistych względów. Ale jest to kod złożony z zafixowanych, niezmiennych klocków i jest produkowany instrukcja po instrukcji, bez brania pod uwagę jakiegokolwiek kontekstu.
JIT generuje nowy kod maszynowy, zwykle na podstawie analizy większego bloku instrukcji (co umożliwia optymalizacje).

0

Podejdźmy do tego od innej strony: jaki program nie generuje kodu maszynowego?

Jak ktoś jest ciekaw czy rzeczywiście interpreter bajtkodu z HotSpota (OpenJDK/ OracleJDK) jest analogiczny do przedstawionego wcześniej interpretera Brainfucka to zapraszam do objerzenia: http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp Jest w nim analogicznie wczytywanie bajtkodu jeden po drugim i wykonywanie odpowiedniego case z wielkiego switcha (ten interpreter Brainfucka co podałem używa drabinki ifów, ale da się trywialnie zamienić na switcha):

#ifndef USELABELS
  while (1)
#endif
  {
#ifndef PREFETCH_OPCCODE
      opcode = *pc; // tutaj wczytujemy kolejny kawałek bajtkodu
#endif
      // Seems like this happens twice per opcode. At worst this is only
      // need at entry to the loop.
      // DEBUGGER_SINGLE_STEP_NOTIFY();
      /* Using this labels avoids double breakpoints when quickening and
       * when returing from transition frames.
       */
  opcode_switch:
      assert(istate == orig, "Corrupted istate");
      /* QQQ Hmm this has knowledge of direction, ought to be a stack method */
      assert(topOfStack >= istate->stack_limit(), "Stack overrun");
      assert(topOfStack < istate->stack_base(), "Stack underrun");

#ifdef USELABELS
      DISPATCH(opcode);
#else
      switch (opcode) // tutaj odpalamy jebutnego switcha, który implementuje semantykę poszczególnych instrukcji bajtkodu
#endif
      {
      CASE(_nop):
          UPDATE_PC_AND_CONTINUE(1);

          /* Push miscellaneous constants onto the stack. */

      CASE(_aconst_null):
          SET_STACK_OBJECT(NULL, 0);
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);

#undef  OPC_CONST_n
#define OPC_CONST_n(opcode, const_type, value)                          \
      CASE(opcode):                                                     \
          SET_STACK_ ## const_type(value, 0);                           \
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);

          OPC_CONST_n(_iconst_m1,   INT,       -1);
          OPC_CONST_n(_iconst_0,    INT,        0);
          OPC_CONST_n(_iconst_1,    INT,        1);
          OPC_CONST_n(_iconst_2,    INT,        2);
          OPC_CONST_n(_iconst_3,    INT,        3);
          OPC_CONST_n(_iconst_4,    INT,        4);
          OPC_CONST_n(_iconst_5,    INT,        5);
          OPC_CONST_n(_fconst_0,    FLOAT,      0.0);
          OPC_CONST_n(_fconst_1,    FLOAT,      1.0);
          OPC_CONST_n(_fconst_2,    FLOAT,      2.0);

#undef  OPC_CONST2_n
#define OPC_CONST2_n(opcname, value, key, kind)                         \
      CASE(_##opcname):                                                 \
      {                                                                 \
          SET_STACK_ ## kind(VM##key##Const##value(), 1);               \
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2);                         \
      }
         OPC_CONST2_n(dconst_0, Zero, double, DOUBLE);
         OPC_CONST2_n(dconst_1, One,  double, DOUBLE);
         OPC_CONST2_n(lconst_0, Zero, long, LONG);
         OPC_CONST2_n(lconst_1, One,  long, LONG);

         /* Load constant from constant pool: */

          /* Push a 1-byte signed integer value onto the stack. */
      CASE(_bipush):
          SET_STACK_INT((jbyte)(pc[1]), 0);
          UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);

          /* Push a 2-byte signed integer constant onto the stack. */
      CASE(_sipush):
          SET_STACK_INT((int16_t)Bytes::get_Java_u2(pc + 1), 0);
          UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);

          /* load from local variable */

      CASE(_aload):
          VERIFY_OOP(LOCALS_OBJECT(pc[1]));
          SET_STACK_OBJECT(LOCALS_OBJECT(pc[1]), 0);
          UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);

      CASE(_iload):
      CASE(_fload):
          SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0);
          UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);

      CASE(_lload):
          SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(pc[1]), 1);
          UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2);

      CASE(_dload):
          SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(pc[1]), 1);
          UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2);

1 użytkowników online, w tym zalogowanych: 0, gości: 1