Java „Virtual Machine” vs. Python „Interpreter”?

207

Rzadko zdarza się czytać o „maszynie wirtualnej” w języku Python, podczas gdy w Javie „maszyna wirtualna” jest używana przez cały czas.

Oba interpretują kody bajtów; po co nazywać jedną maszynę wirtualną, a drugą tłumaczem?

twile
źródło

Odpowiedzi:

137

Maszyna wirtualna jest wirtualnym środowiskiem komputerowym z określonym zestawem dobrze zdefiniowanych instrukcji atomowych, które są obsługiwane niezależnie od określonego języka i na ogół jest uważana za piaskownicę samą w sobie. Maszyna wirtualna jest analogiczna do zestawu instrukcji określonego procesora i ma tendencję do działania na bardziej podstawowym poziomie z bardzo podstawowymi elementami składowymi takich instrukcji (lub kodów bajtów), które są niezależne od następnego. Instrukcja jest wykonywana deterministycznie tylko na podstawie bieżącego stanu maszyny wirtualnej i nie zależy od informacji w innym miejscu strumienia instrukcji w tym momencie.

Z drugiej strony interpreter jest bardziej wyrafinowany, ponieważ jest dostosowany do analizowania strumienia pewnej składni, która jest w określonym języku i określonego gramatyki, którą należy zdekodować w kontekście otaczających tokenów. Nie możesz patrzeć na każdy bajt, a nawet na każdą linię osobno i dokładnie wiedzieć, co dalej. Tokenów w języku nie można rozpatrywać w oderwaniu, ponieważ mogą one odnosić się do instrukcji (kodów bajtów) maszyny wirtualnej.

Kompilator Java konwertuje język Java na strumień kodu bajtowego, podobnie jak kompilator C konwertuje programy języka C na kod asemblera. Z drugiej strony interpreter tak naprawdę nie przekształca programu w żadną dobrze zdefiniowaną formę pośrednią, po prostu przyjmuje działania programu w ramach procesu interpretacji źródła.

Kolejnym testem różnicy między maszyną wirtualną a tłumaczem jest to, czy uważasz ją za niezależną od języka. To, co znamy jako wirtualna maszyna Java, tak naprawdę nie jest specyficzne dla Java. Można utworzyć kompilator z innych języków, w których powstają kody bajtów, które można uruchomić w JVM. Z drugiej strony nie sądzę, abyśmy naprawdę myśleli o „skompilowaniu” innego języka niż Python do Pythona w celu interpretacji przez interpretera Pythona.

Ze względu na złożoność procesu tłumaczenia, może to być stosunkowo powolny proces .... w szczególności analizowanie i identyfikowanie tokenów językowych itp. Oraz rozumienie kontekstu źródła, aby móc wykonać proces wykonania w tłumaczu. Aby przyspieszyć takie interpretowane języki, tutaj możemy zdefiniować pośrednie formy wstępnie przeanalizowanego, wstępnie tokenizowanego kodu źródłowego, który jest łatwiejszy do bezpośredniej interpretacji. Ten rodzaj postaci binarnej jest nadal interpretowany w czasie wykonywania, po prostu zaczyna się od postaci mniej czytelnej dla człowieka, aby poprawić wydajność. Jednak logika wykonująca ten formularz nie jest maszyną wirtualną, ponieważ tych kodów nadal nie można rozpatrywać w oderwaniu - kontekst otaczających tokenów nadal ma znaczenie, są one teraz w innej, bardziej wydajnej komputerowo formie.

Wysoki Jeff
źródło
7
Miałem wrażenie, że Python wygenerował kod bajtowy, pyc, lub że to, o czym mówisz, „pomaga przyspieszyć takie interpretowane języki, to tutaj możemy zdefiniować pośrednie formy wstępnie przeanalizowanego, wstępnie tokenizowanego kodu źródłowego, który jest bardziej łatwo interpretowane bezpośrednio ”.
James McMahon
32
@InSciTek Jeff: Z twojej odpowiedzi nie wiadomo, czy wiesz, że Python również używa maszyny wirtualnej.
tzot
3
@TZ - Popularną implementacją języka Python jest kompilator języka Python z maszyną wirtualną po stronie tylnej. W trybie interaktywnym jest to trochę hybryda z interfejsem interpretera i zapleczem kompilatora. Są to jednak opcje wdrażania. Próbowałem opisać różnicę między koncepcją VM a tłumaczem
Tall Jeff
8
On the other hand, I don't think we would really think of "compiling" some other language other than Python into Python for interpretation by the Python interpreter.Możliwe jest napisanie języka, który można skompilować do kodu bajtowego Pythona, podobnie jak Scala jest skompilowany do kodu bajtowego Java. W trybie interaktywnym powłoka interaktywna Pythona kompiluje wpisane polecenie do kodu bajtowego i wykonuje ten kod bajtowy. Możesz napisać własną powłokę za pomocą eval i exec, a także możesz użyć wbudowanej funkcji compile (), aby zamienić ciąg znaków na kod bajtowy.
Lie Ryan,
4
@Lie Ryan tak, ale nie jest oficjalnie obsługiwany tak jak w JVM. W Pythonie kod bajtowy jest nieudokumentowanym szczegółem implementacji.
Antymon
159

W tym poście „maszyna wirtualna” odnosi się do maszyn wirtualnych, a nie do maszyn wirtualnych systemu, takich jak Qemu lub Virtualbox. Procesowa maszyna wirtualna to po prostu program zapewniający ogólne środowisko programistyczne - program, który można zaprogramować.

Java ma zarówno tłumacza, jak i maszynę wirtualną, a Python ma maszynę wirtualną i tłumacza. Powód, dla którego „maszyna wirtualna” jest bardziej powszechnym terminem w Javie, a „interpreter” jest bardziej powszechnym terminem w Pythonie, ma wiele wspólnego z główną różnicą między tymi dwoma językami: pisaniem statycznym (Java) a pisaniem dynamicznym (Python). W tym kontekście „typ” odnosi się do prymitywnych typów danych - typów, które sugerują rozmiar danych w pamięci. Wirtualna maszyna Java ma to łatwe. Wymaga od programisty określenia pierwotnego typu danych każdej zmiennej. Zapewnia to wystarczające informacje dla kodu bajtowego Java nie tylko do interpretacji i wykonania przez maszynę wirtualną Java, ale nawet do skompilowania w instrukcje maszyny. Maszyna wirtualna Python jest bardziej złożona w tym sensie, że podejmuje dodatkowe zadanie wstrzymania przed wykonaniem każdej operacji w celu określenia pierwotnych typów danych dla każdej zmiennej lub struktury danych zaangażowanych w operację. Python uwalnia programistę od myślenia o prymitywnych typach danych i umożliwia wyrażanie operacji na wyższym poziomie. Ceną tej wolności jest wydajność. „Interpreter” jest preferowanym terminem dla Pythona, ponieważ musi się zatrzymać, aby sprawdzić typy danych, a także dlatego, że stosunkowo zwięzła składnia języków dynamicznie typowanych dobrze pasuje do interaktywnych interfejsów. Nie ma technicznych barier w budowaniu interaktywnego interfejsu Java, ale próba interaktywnego pisania statycznego kodu byłaby żmudna, więc po prostu nie jest to zrobione w ten sposób.

W świecie Java maszyna wirtualna kradnie program, ponieważ uruchamia programy napisane w języku, który można tak naprawdę skompilować w instrukcje maszyny, a rezultatem jest szybkość i wydajność zasobów. Kod bajtowy Java może być wykonywany przez maszynę wirtualną Java z wydajnością zbliżoną do wydajności skompilowanych programów. Wynika to z obecności prymitywnej informacji o typie danych w kodzie bajtowym. Maszyna wirtualna Java umieszcza Javę w swojej kategorii:

przenośny interpretowany język pisany statycznie

Następną najbliższą rzeczą jest LLVM, ale LLVM działa na innym poziomie:

przenośny interpretowany język asemblera

Termin „kod bajtowy” jest używany zarówno w Javie, jak i Pythonie, ale nie wszystkie kody bajtowe są sobie równe. bytecode to tylko ogólny termin dla języków pośrednich używanych przez kompilatory / tłumaczy. Nawet kompilatory C, takie jak gcc, używają języka pośredniego (lub kilku), aby wykonać zadanie. Kod bajtowy Java zawiera informacje o prymitywnych typach danych, natomiast kod bajtowy Pythona nie. Pod tym względem maszyna wirtualna Python (i Bash, Perl, Ruby itp.) Naprawdę jest zasadniczo wolniejsza niż maszyna wirtualna Java, a raczej po prostu wymaga więcej pracy. Warto rozważyć, jakie informacje są zawarte w różnych formatach kodu bajtowego:

  • llvm: rejestry procesora
  • Java: prymitywne typy danych
  • Python: typy zdefiniowane przez użytkownika

Aby narysować prawdziwą analogię: LLVM działa z atomami, wirtualna maszyna Java działa z cząsteczkami, a wirtualna maszyna Python z materiałami. Ponieważ wszystko musi ostatecznie ulec rozkładowi na cząsteczki subatomowe (operacje na prawdziwych maszynach), maszyna wirtualna Python ma najbardziej złożone zadanie.

Interpretatorzy / kompilatory języków o typie statycznym po prostu nie mają tego samego bagażu, co tłumacze / kompilatory języków o typie dynamicznym. Programiści języków o typie statycznym muszą liczyć się z luzem, za który wypłaca się wydajność. Jednak podobnie jak wszystkie funkcje niedeterministyczne są potajemnie deterministyczne, podobnie wszystkie dynamicznie typowane języki są tajnie statycznie wpisywane. Różnice w wydajności między dwiema rodzinami języków powinny zatem wyrównać się w czasie, gdy Python zmienia nazwę na HAL 9000.

Maszyny wirtualne w dynamicznych językach, takie jak Python, implementują jakąś wyidealizowaną maszynę logiczną i niekoniecznie ściśle odpowiadają rzeczywistemu sprzętowi fizycznemu. Natomiast wirtualna maszyna Java jest bardziej podobna funkcjonalnie do klasycznego kompilatora C, z tym wyjątkiem, że zamiast emitować instrukcje maszyny, wykonuje wbudowane procedury. W Pythonie liczba całkowita jest obiektem Pythona z dołączoną wiązką atrybutów i metod. W Javie int jest wyznaczoną liczbą bitów, zwykle 32. To nie jest tak naprawdę uczciwe porównanie. Liczby całkowite w języku Python należy naprawdę porównać z klasą Java Integer. Prymitywny typ danych Java „int” nie może być porównywany z niczym w języku Python, ponieważ w języku Python po prostu brakuje tej warstwy prymitywów, podobnie jak kod bajtowy Pythona.

Ponieważ zmienne Java są jawnie wpisywane, można rozsądnie oczekiwać, że coś takiego jak wydajność Jython znajdzie się w tym samym ballparku co cPython . Z drugiej strony wirtualna maszyna Java zaimplementowana w Pythonie jest prawie na pewno wolniejsza niż błoto. I nie spodziewaj się, że Ruby, Perl itp. Poradzą sobie lepiej. Nie zostali do tego stworzeni. Zostały zaprojektowane do „skryptowania”, czyli tak nazywane jest programowanie w języku dynamicznym.

Każda operacja, która ma miejsce na maszynie wirtualnej, musi ostatecznie trafić na prawdziwy sprzęt. Maszyny wirtualne zawierają wstępnie skompilowane procedury, które są wystarczająco ogólne, aby wykonać dowolną kombinację operacji logicznych. Maszyna wirtualna może nie emitować instrukcji nowej maszyny, ale z pewnością wykonuje swoje własne procedury w arbirtralnie złożonych sekwencjach. Maszyna wirtualna Java, maszyna wirtualna Python i wszystkie inne maszyny wirtualne ogólnego przeznaczenia są równe w tym sensie, że można je nakłonić do wykonania dowolnej logiki, o jakiej można marzyć, ale różnią się pod względem zadań, które wykonują podjąć się i jakie zadania pozostawiają programistom.

Psyco for Python nie jest pełną maszyną wirtualną Python, ale kompilatorem just-in-time, który przechwytuje zwykłą maszynę wirtualną Python w punktach, w których myśli, że może skompilować kilka linii kodu - głównie pętle, w których uważa prymitywny typ niektórych zmienna pozostanie stała, nawet jeśli wartość zmienia się z każdą iteracją. W takim przypadku może zrezygnować z niektórych nieoczekiwanych metod określania zwykłej maszyny wirtualnej. Musisz jednak zachować ostrożność, aby nie wyciągnąć tego typu spod stóp Psyco. Jednak Pysco zwykle wie, aby po prostu wrócić do zwykłej maszyny wirtualnej, jeśli nie jest całkowicie pewien, czy typ się nie zmieni.

Morał tej historii jest taki, że prymitywne informacje o typie danych są naprawdę pomocne dla kompilatora / maszyny wirtualnej.

Na koniec, biorąc to wszystko w perspektywie, rozważ to: program Python wykonywany przez interpreter / maszynę wirtualną Python zaimplementowaną w Javie działający na interpreter / maszynę wirtualną Java zaimplementowaną w LLVM działający na maszynie wirtualnej qemu działającej na iPhonie.

bezpośredni link

pooryorick
źródło
1
trying to write any statically-typed code interactively would be tedious. Jeśli znasz OCaml i Haskell, zobaczysz, że to nieprawda, ponieważ są to bardzo zwięzłe, statycznie pisane języki.
Matthias Braun
@MatthiasBraun Mogę się zgodzić, że te funkcjonalne języki tworzą zwięzły kod, ale to niekoniecznie oznacza, że ​​dobrze nadają się do trybu interaktywnego. Gdyby OCaml i Haskell były dynamicznie pisane jak seplenienie, działałyby lepiej w trybie interaktywnym, jak zakładam.
bomby
58

Prawdopodobnie jednym z powodów odmiennej terminologii jest to, że zwykle myśli się o karmieniu interpretera języka Python surowym czytelnym dla człowieka kodzie źródłowym i nie martwiąc się kodem bajtowym i tym podobne.

W Javie musisz jawnie skompilować kod bajtowy, a następnie uruchomić tylko kod bajtowy, a nie kod źródłowy na maszynie wirtualnej.

Mimo że Python korzysta z maszyny wirtualnej pod przykryciem, z perspektywy użytkownika można zignorować ten szczegół przez większość czasu.

Pan Fooz
źródło
1
Zgadzam się. Ta różnica w terminologii naprawdę sprowadza się do doświadczenia użytkownika końcowego (czyli programisty). Nie ma to nic wspólnego z prawdziwymi różnicami technicznymi, ponieważ linia techniczna jest tak niewiarygodnie niewyraźna, że ​​prawie nie istnieje.
Cody Brocious
1
+1: I - co ważniejsze - jaki jest sens? Jakiego programu nie możesz napisać z tego powodu? Jaki ślad śledzenia stosu jest dla ciebie mylący? Jaka biblioteka wydaje się nie działać poprawnie?
S.Lott
@ S.Lott Ponieważ zawsze dobrze jest wygrywać spory z kolegami. ;)
Qix - MONICA MISTREATED
16

Tłumacz , tłumaczy kod źródłowy na pewną wydajną reprezentację pośrednią (kod) i natychmiast wykonuje to.

Maszyna wirtualna jawnie wykonuje zapisany wstępnie skompilowany kod zbudowany przez kompilator będący częścią systemu tłumacza.

Bardzo ważną cechą maszyny wirtualnej jest to, że oprogramowanie wewnątrz niej jest ograniczone do zasobów udostępnianych przez maszynę wirtualną. Dokładnie nie może wyrwać się ze swojego wirtualnego świata. Pomyśl o bezpiecznym wykonaniu zdalnego kodu, apletów Java.

W przypadku pytona, jeśli trzymamy pyc pliki , jak wspomniano w komentarzu do tego postu, wówczas mechanizm stałby się bardziej jak maszyna wirtualna, a ten kod bajtowy działa szybciej - nadal byłby interpretowany, ale z bardziej przyjaznej dla komputera formy . Jeśli spojrzymy na to jako całość, PVM jest ostatnim krokiem interpretera Pythona.

Najważniejsze jest to, że gdy odsyłamy Python Interpreter, oznacza to, że odwołujemy się do niego jako całości, a kiedy mówimy PVM, oznacza to, że mówimy tylko o części Python Interpreter, środowisku wykonawczym. Podobnie jak w Javie, odnosimy się do różnych części differentyl, JRE, JVM, JDK itp.

Aby uzyskać więcej informacji, wpis na Wikipedii: tłumacz i maszyna wirtualna . Jeszcze jeden tutaj . Tutaj możesz znaleźć porównanie maszyn wirtualnych aplikacji . Pomaga zrozumieć różnicę między kompilatorami, tłumaczami i maszynami wirtualnymi.

Adeel Ansari
źródło
12

Termin interpreter jest starszym terminem pochodzącym z wcześniejszych języków skryptowych powłoki. Ponieważ „języki skryptowe” ewoluowały w pełni funkcjonalne języki, a odpowiadające im platformy stały się bardziej wyrafinowane i piaskownicy, rozróżnienie między maszyną wirtualną a tłumaczem (w sensie Pythona) jest bardzo małe lub nie istnieje.

Interpretator Pythona nadal działa w taki sam sposób jak skrypt powłoki, w tym sensie, że można go wykonać bez osobnego kroku kompilacji. Poza tym różnice między tłumaczem Pythona (Perla lub Ruby) a maszyną wirtualną Javy są głównie szczegółami implementacji. (Można argumentować, że Java jest w pełni piaskownicą niż Python, ale oba ostatecznie zapewniają dostęp do podstawowej architektury za pośrednictwem rodzimego interfejsu C.)

Daniel Naab
źródło
1
istnieją powłoki Java, które mogą uruchamiać kod Java bez oddzielnych (widocznych dla użytkownika) kroków kompilacji.
Lie Ryan
1
daj mi imię: D
Maciej Nowicki
11

Aby udzielić głębokiej odpowiedzi na pytanie „ Dlaczego Java Virtual Machine, a interpreter Pythona? ”, Spróbujmy wrócić do teorii kompilacji, która jest punktem wyjścia dyskusji.

Typowy proces kompilacji programu obejmuje kolejne kroki:

  1. Analiza leksykalna . Dzieli tekst programu na znaczące „słowa” zwane tokenami (w ramach procesu usuwane są wszystkie komentarze, spacje, znaki nowej linii itp., Ponieważ nie wpływają one na zachowanie programu). Wynikiem jest uporządkowany strumień tokenów.
  2. Analiza składniowa . Buduje tak zwane abstrakcyjne drzewo składniowe (AST) ze strumienia tokeny. AST ustanawia relacje między tokenami, aw konsekwencji określa kolejność oceny programu.
  3. Analiza semantyczna . Weryfikuje poprawność semantyczną AST za pomocą informacji o typach i zestawie reguł semantycznych języka programowania. (Na przykład a = b + cjest poprawną instrukcją z punktu widzenia składni, ale całkowicie niepoprawną z semantycznego punktu widzenia, jeśli azostała zadeklarowana jako obiekt stały)
  4. Pośrednie generowanie kodu . Serializuje AST do liniowo uporządkowanego strumienia niezależnych od maszyny operacji „prymitywnych”. W rzeczywistości generator kodu przechodzi przez AST i rejestruje kolejność etapów oceny. W rezultacie z drzewiastej reprezentacji programu uzyskujemy znacznie prostszą reprezentację listową, w której zachowana jest kolejność oceny programu.
  5. Generowanie kodu maszynowego . Program w postaci niezależnego od maszyny „prymitywnego” kodu bajtowego jest tłumaczony na kod maszynowy konkretnej architektury procesora.

Dobrze. Pozwala teraz zdefiniować warunki.

Tłumacz ustny w klasycznym znaczeniu tego słowa zakłada wykonanie oparte na ocenie programu na podstawie AST utworzonej bezpośrednio z tekstu programu . W takim przypadku program jest dystrybuowany w postaci kodu źródłowego, a tłumacz jest zasilany tekstem programu, często w sposób dynamiczny (instrukcja po instrukcji lub linia po linii). Dla każdej instrukcji wejściowej interpreter buduje swój AST i natychmiast ocenia go, zmieniając „stan” programu. Jest to typowe zachowanie demonstrowane przez języki skryptowe. Rozważmy na przykład Bash, Windows CMD itp. Pod względem koncepcyjnym Python również to robi.

Jeśli zastąpimy etap wykonywania oparty na AST podczas generowania pośredniego niezależnego od maszyny binarnego kroku kodu bajtowego w interpreterie, podzielimy cały proces wykonywania programu na dwie oddzielne fazy: kompilację i wykonanie. W takim przypadku wcześniejszy interpreter stanie się kompilatorem kodu bajtowego, który przekształci program z postaci tekstu w jakąś formę binarną . Następnie program jest dystrybuowany w tej formie binarnej, ale nie w postaci kodu źródłowego. Na maszynie użytkownika ten bajtkod jest wprowadzany do nowej encji - maszyny wirtualnej , która faktycznie interpretuje ten bajtkod. Z tego powodu maszyny wirtualne są również nazywane interpreterem kodu bajtowego . Ale skup swoją uwagę tutaj! Klasyczny tłumacz tointerpreter tekstu , ale maszyna wirtualna tointerpreter binarny ! Jest to podejście przyjęte przez Javę i C #.

Wreszcie, jeśli dodamy generowanie kodu maszynowego do kompilatora kodu bajtowego, osiągniemy w rezultacie to, co nazywamy kompilatorem klasycznym . Klasyczny kompilator konwertuje kod źródłowy programu na kod maszynowy konkretnego procesora. Ten kod maszynowy można następnie wykonać bezpośrednio na procesorze docelowym bez dodatkowej mediacji (bez żadnego interpretera ani interpretera tekstu ani interpretera binarnego).

Wróćmy teraz do pierwotnego pytania i rozważmy Java vs Python.

Java została początkowo zaprojektowana tak, aby mieć jak najmniej zależności implementacyjnych. Jego konstrukcja opiera się na zasadzie „pisz raz, biegnij gdziekolwiek” (WORA). Aby go zaimplementować, Java została początkowo zaprojektowana jako język programowania, który kompiluje się w niezależny od maszyny binarny kod bajtowy , który następnie można wykonać na wszystkich platformach obsługujących Javę bez potrzeby jego ponownej kompilacji. Możesz myśleć o Javie jak o C ++ opartym na WORA . W rzeczywistości Java jest bliższa C ++ niż językom skryptowym takim jak Python . Ale w przeciwieństwie do C ++ , Javazostał zaprojektowany do kompilacji dobinarny kod bajtowy, który jest następnie wykonywany w środowisku maszyny wirtualnej , podczas gdy C ++ został zaprojektowany do kompilacji w kodzie maszynowym, a następnie bezpośrednio wykonywany przez procesor docelowy.

Python został początkowo zaprojektowany jako rodzaj skryptowego języka programowania, który interpretuje skrypty (programy w formie tekstu napisanego zgodnie z regułami języka programowania). Z tego powodu Python początkowo obsługiwał dynamiczną interpretację poleceń lub instrukcji jednowierszowych, podobnie jak Bash lub Windows CMD. Z tego samego powodu początkowe implementacje Pythona nie zawierały żadnych kompilatorów bajtów i maszyn wirtualnych do wykonania takiego kodu bajtowego, ale od samego początku Python wymagał interpretera, który jest w stanie zrozumieć i ocenić tekst programu Python .

Z tego powodu historycznie programiści Java zwykle mówili o wirtualnej maszynie Java (ponieważ początkowo Java pojawiła się jako pakiet kompilatora kodu bajtowego Java i interpretera kodu bajtowego - JVM ), a programiści Python zwykle mówili o interprecie języka Python (ponieważ początkowo Python miał żadna maszyna wirtualna i był rodzajem klasycznego interpretera tekstu, który wykonuje tekst programu bezpośrednio, bez jakiejkolwiek kompilacji lub transformacji w jakąkolwiek formę kodu binarnego).

Obecnie Python ma również maszynę wirtualną pod maską i może kompilować i interpretować kod bajtowy Pythona. Fakt ten stanowi dodatkową inwestycję w zamieszanie: „ Dlaczego Java Virtual Machine, a interpreter Pythona?i że programy wykażą dokładnie to samo zachowanie i wygenerują jednakowo takie same wyniki z jednakowego wkładu. Jedyną zauważalną różnicą będzie szybkość wykonywania programu i ilość pamięci zajętej przez tłumacza. Zatem maszyna wirtualna w Pythonie nie jest nieuniknioną częścią projektu językowego, ale jest jedynie opcjonalnym rozszerzeniem głównego interpretera Pythona.

Java może być traktowana w podobny sposób. Java pod maską ma kompilator JIT i może selektywnie kompilować metody klasy Java do kodu maszynowego platformy docelowej, a następnie bezpośrednio ją wykonać. Ale! Java nadal wykorzystuje interpretację kodu bajtowego jako podstawowy sposób wykonywania programu Java. Podobnie jak implementacje Python, które wykorzystują maszyny wirtualne pod maską wyłącznie jako technika optymalizacji, maszyny wirtualne Java używają kompilatorów Just-In-Time wyłącznie do celów optymalizacji. Podobnie tylko dlatego, że bezpośrednie wykonanie kodu maszynowego jest co najmniej dziesięć razy szybsze niż interpretacja kodu bajtowego Java. I podobnie jak w przypadku Pythona, obecność kompilatora JIT pod maską JVM jest całkowicie przejrzysta zarówno dla projektantów języka Java, jak i twórców programów Java. JVM może implementować ten sam język programowania Java z kompilatorem JIT i bez niego. I w ten sam sposób te same programy mogą być uruchamiane w JVM z JIT i bez JIT, a te same programy będą wykazywać dokładnie to samo zachowanie i wytwarzać jednakowo takie same wyniki z równego wejścia na obu JVM (z JIT i bez JIT). I podobnie jak w przypadku Pythona, jedyną zauważalną różnicą między nimi będzie szybkość wykonania i ilość pamięci zużywanej przez JVM. I wreszcie, podobnie jak w przypadku Pythona, JIT w Javie również nie jest nieuniknioną częścią projektowania języka, ale jest opcjonalnym rozszerzeniem głównych implementacji JVM. a te same programy wykażą dokładnie to samo zachowanie i wygenerują jednakowo takie same wyniki z jednakowego wejścia na obu maszynach JVM (z JIT i bez JIT). I podobnie jak w przypadku Pythona, jedyną zauważalną różnicą między nimi będzie szybkość wykonania i ilość pamięci zużywanej przez JVM. I wreszcie, podobnie jak w przypadku Pythona, JIT w Javie również nie jest nieuniknioną częścią projektowania języka, ale jest opcjonalnym rozszerzeniem głównych implementacji JVM. a te same programy wykażą dokładnie to samo zachowanie i wygenerują jednakowo takie same wyniki z jednakowego wejścia na obu maszynach JVM (z JIT i bez JIT). I podobnie jak w przypadku Pythona, jedyną zauważalną różnicą między nimi będzie szybkość wykonania i ilość pamięci zużywanej przez JVM. I wreszcie, podobnie jak w przypadku Pythona, JIT w Javie również nie jest nieuniknioną częścią projektowania języka, ale jest opcjonalnym rozszerzeniem głównych implementacji JVM.

Z punktu widzenia projektowania i implementacji maszyn wirtualnych Java i Python różnią się one znacznie, podczas gdy (uwaga!) Oba nadal pozostają maszynami wirtualnymi. JVM jest przykładem maszyny wirtualnej niskiego poziomu z prostymi podstawowymi operacjami i wysokimi kosztami wysyłki instrukcji. Z kolei Python jest maszyną wirtualną wysokiego poziomu, dla której instrukcje wykazują złożone zachowanie, a koszt wysyłki instrukcji nie jest tak znaczący. Java działa z bardzo niskim poziomem abstrakcji. JVM działa na małym, dobrze zdefiniowanym zestawie typów pierwotnych i ma bardzo ścisłą zgodność (zazwyczaj jeden do jednego) między instrukcjami kodu bajtowego a instrukcjami natywnego kodu maszynowego. Przeciwnie, maszyna wirtualna Python działa na wysokim poziomie abstrakcji, działa ze złożonymi typami danych (obiektami) i obsługuje polimorfizm ad-hoc, podczas gdy instrukcje kodu bajtowego ujawniają złożone zachowanie, które może być reprezentowane przez szereg wielu natywnych instrukcji kodu maszynowego. Na przykład Python obsługuje matematykę o nieograniczonym zakresie. Tak więc Python VM jest zmuszony wykorzystywać długą arytmetykę dla potencjalnie dużych liczb całkowitych, dla których wynik operacji może przepełnić słowo maszynowe. Zatem jedna instrukcja kodu bajtowego dla arytmetyki w Pythonie może ujawniać się w wywołaniu funkcji w Pythonie VM, podczas gdy w JVM operacja arytmetyczna ujawnia się jako prosta operacja wyrażona przez jedną lub kilka natywnych instrukcji maszynowych. Tak więc Python VM jest zmuszony wykorzystywać długą arytmetykę dla potencjalnie dużych liczb całkowitych, dla których wynik operacji może przepełnić słowo maszynowe. Zatem jedna instrukcja kodu bajtowego dla arytmetyki w Pythonie może ujawniać się w wywołaniu funkcji w Pythonie VM, podczas gdy w JVM operacja arytmetyczna ujawnia się jako prosta operacja wyrażona przez jedną lub kilka natywnych instrukcji maszynowych. Tak więc Python VM jest zmuszony wykorzystywać długą arytmetykę dla potencjalnie dużych liczb całkowitych, dla których wynik operacji może przepełnić słowo maszynowe. Zatem jedna instrukcja kodu bajtowego dla arytmetyki w Pythonie może ujawniać się w wywołaniu funkcji w Pythonie VM, podczas gdy w JVM operacja arytmetyczna ujawnia się jako prosta operacja wyrażona przez jedną lub kilka natywnych instrukcji maszynowych.

W rezultacie możemy wyciągnąć kolejne wnioski. Java Virtual Machine, ale interpreter języka Python jest taki, że:

  1. Pojęcie maszyny wirtualnej zakłada binarną interpretację kodu bajtowego, a pojęcie tłumacza zakłada interpretację tekstu programu.
  2. Historycznie Java została zaprojektowana i wdrożona do interpretacji binarnego kodu bajtowego, a Python został początkowo zaprojektowany i wdrożony do interpretacji tekstu programu. Tak więc termin „wirtualna maszyna Java” jest historyczną i dobrze ugruntowaną społecznością Java. Podobnie, termin „interpreter języka Python” jest historyczny i dobrze ugruntowany w społeczności Python. Ludzie mają tendencję do przedłużania tradycji i używają tych samych terminów, które były używane wcześniej.
  3. Wreszcie, dla Java, interpretacja binarnego kodu bajtowego jest podstawowym sposobem wykonywania programów, podczas gdy kompilacja JIT jest tylko opcjonalną i przejrzystą optymalizacją. A dla Pythona obecnie interpretacja tekstu programu jest podstawowym sposobem wykonywania programów w Pythonie, podczas gdy kompilacja do kodu bajtowego Python VM jest tylko opcjonalną i przejrzystą optymalizacją.

Dlatego zarówno Java, jak i Python mają maszyny wirtualne jako binarne interpretery kodu bajtowego, co może prowadzić do nieporozumień, takich jak „ Dlaczego Java Virtual Machine, ale interpreter Pythona?”. Kluczową kwestią jest to, że dla Pythona maszyna wirtualna nie jest podstawowym ani niezbędnym środkiem wykonywania programu; jest tylko opcjonalnym rozszerzeniem klasycznego interpretera tekstu. Z drugiej strony maszyna wirtualna jest podstawowym i nieuniknionym elementem ekosystemu wykonywania programów Java. Wybór typowania statycznego lub dynamicznego do projektowania języka programowania wpływa głównie tylko na poziom abstrakcji maszyny wirtualnej, ale nie decyduje, czy maszyna wirtualna jest potrzebna. Języki wykorzystujące oba systemy pisania mogą być zaprojektowane do kompilacji, interpretacji lub wykonywania w środowisku maszyny wirtualnej, w zależności od pożądanego modelu wykonywania.

ZarathustrA
źródło
2
Należy to wybrać jako oficjalną odpowiedź IMHO.
Ravikanth Andhavarapu
Oficjalna odpowiedź powinna brzmieć TAK, ponieważ „zarówno Java, jak i Python mają maszyny wirtualne jako binarne interpretery kodu bajtowego”. Kropka.
stuartw
10

Nie ma między nimi prawdziwej różnicy, ludzie po prostu przestrzegają konwencji wybranych przez twórców.

Cody Brocious
źródło
3
Rzucę ci tutaj kość, ponieważ myślę, że to prawdopodobnie prawdziwa odpowiedź, a ty głosowałeś za brakiem bitów.
vikingben
3

Nie zapominaj, że w Pythonie dostępne są kompilatory JIT dla x86, co dodatkowo dezorientuje problem. (Zobacz psyco).

Bardziej rygorystyczna interpretacja „języka interpretowanego” staje się użyteczna tylko przy omawianiu problemów z wydajnością maszyny wirtualnej, na przykład w porównaniu z Pythonem Ruby był (jest?) Uważany za wolniejszy, ponieważ jest to język interpretowany, w przeciwieństwie do Pythona - w innych słowa, kontekst jest wszystkim.

Arafangion
źródło
1
To jest źle. Po pierwsze, nie ma czegoś takiego jak „język interpretowany”. To, czy implementacja korzysta z kompilatora czy interpretera, nie jest cechą języka, ale implementacji. Po drugie, z około 13 implementacji Ruby dokładnie 1 jest tłumaczem, wszystkie pozostałe mają kompilatory.
Jörg W Mittag
2
Po trzecie, Ruby nie jest powolna. Żaden język nie jest wolny, ponieważ szybkość nie jest cechą języka, ale jego implementacją. Z około 13 implementacji Ruby niektóre są wolniejsze niż niektóre z 7 implementacji Pythona, inne są szybsze.
Jörg W Mittag
Myślę, że porównuje tutaj standardowe wdrożenia Jörg. CPython i Ruby (myślę, że oficjalna implementacja to właśnie Ruby).
James McMahon
Chociaż Arafangion mógł odnosić się do „standardowych” implementacji, powinien był to powiedzieć. Jestem Pythonistą, ale nienawidzę jakiegokolwiek wyrażenia w formie „Język X jest wolny”, ponieważ zgadzam się z Jörgiem w kwestii implementacji.
tzot
1
Właśnie dlatego powiedziałem „był (jest?)”, A zwłaszcza termin „wolniej”. Nigdzie nie powiedziałem, że Ruby sama w sobie jest powolna.
Arafangion
2

Python może interpretować kod bez kompilacji go do kodu bajtowego. Java nie może .

Python jest językiem interpretowanym, w przeciwieństwie do skompilowanego, chociaż rozróżnienie może być rozmyte z powodu obecności kompilatora kodu bajtowego. Oznacza to, że pliki źródłowe można uruchamiać bezpośrednio, bez jawnego tworzenia pliku wykonywalnego, który jest następnie uruchamiany.

(z dokumentacji).

W Javie każdy pojedynczy plik musi zostać skompilowany do .classpliku, który następnie uruchamia się w JVM. Przeciwnie, python robi to, które są importowane przez twój główny skrypt, aby przyspieszyć kolejne użycie tych plików.

Jednak w typowym przypadku większość kodu Pythona (przynajmniej CPython) działa na emulowanym komputerze stosowym, który ma prawie identyczne instrukcje jak instrukcje JVM, więc nie ma dużej różnicy.

Prawdziwym powodem tego rozróżnienia jest jednak to, że java od samego początku określała się jako „przenośny, wykonywalny kod bajtowy”, a python jako dynamiczny, interpretowany język z REPL. Nazwy się trzymają!

niebieska notatka
źródło
0

Przede wszystkim powinieneś zrozumieć, że programowanie lub informatyka w ogóle nie są matematyką i nie mamy rygorystycznych definicji większości terminów, których często używamy.

teraz na twoje pytanie:

co to jest tłumacz ustny (w informatyce)

Tłumaczy kod źródłowy według najmniejszej wykonywalnej jednostki, a następnie wykonuje tę jednostkę.

co to jest maszyna wirtualna

w przypadku JVM maszyna wirtualna jest oprogramowaniem zawierającym interpreter, moduły ładujące klasy, moduł odśmiecania, harmonogram wątków, kompilator JIT i wiele innych rzeczy.

jak widać interpreter jest częścią lub maszyną JVM, a całej maszyny JVM nie można nazwać tłumaczem, ponieważ zawiera wiele innych komponentów.

dlaczego używamy słowa „tłumacz”, mówiąc o pythonie

w java część kompilacji jest jawna. z drugiej strony python nie jest jawny, ponieważ java dotyczy jego procesu kompilacji i interpretacji, z perspektywy użytkownika końcowego interpretacja jest jedynym mechanizmem używanym do uruchamiania programów w języku Python

mightyWOZ
źródło
0

Nie, oba nie interpretują kodu bajtowego.

Python interpretuje kod bajtowy tylko wtedy, gdy używasz pypy. W przeciwnym razie jest kompilowany do C i interpretowany na tym poziomie.

Java kompiluje się do kodu bajtowego.

Michael Tamillow
źródło
Czy możesz podać jakieś środki na swoją odpowiedź?
Isuru Dilshan
i to jest na Javie: en.wikipedia.org/wiki/Java_virtual_machine
Michael Tamillow
To jest nie tak z przepełnieniem stosu. Ktoś jest wkurzony, ponieważ jest wzywany i wyraża to w głosowaniu negatywnym.
Michael Tamillow
0

Myślę, że linie między nimi są niewyraźne, ludzie w większości spierają się o znaczenie słowa „tłumacz” i jak blisko język stoi po każdej stronie spektrum „tłumacz”. Jednak żaden nie stanowi 100%. Myślę, że łatwo jest napisać implementację Java lub Python, która ma dowolną wartość w spektrum.

Obecnie zarówno Java, jak i Python mają maszyny wirtualne i kod bajtowy, chociaż jeden działa według konkretnych rozmiarów wartości (takich jak 32-bitowa liczba całkowita), podczas gdy drugi musi określać rozmiar każdego wywołania, co moim zdaniem nie określa granicy między warunkami.

Argument, że Python nie ma oficjalnie zdefiniowanego kodu bajtowego i istnieje tylko w pamięci, również mnie nie przekonuje, tylko dlatego, że planuję opracować urządzenia, które rozpoznają tylko kod bajtowy Pythona, a część kompilacji zostanie wykonana na maszynie JS przeglądarki.

Wydajność dotyczy tylko konkretnej implementacji. Nie musimy znać wielkości obiektu, aby móc z nim pracować, a na koniec w większości przypadków pracujemy ze strukturami, a nie podstawowymi typami. Możliwe jest zoptymalizowanie maszyny wirtualnej Python w taki sposób, aby wyeliminowała potrzebę tworzenia nowego obiektu za każdym razem podczas obliczania wyrażenia, poprzez ponowne użycie istniejącego. Po wykonaniu tej operacji nie ma globalnej różnicy w wydajności między obliczaniem sumy dwóch liczb całkowitych, czyli tam, gdzie świeci Java.

Nie ma między nimi różnicy zabójców, tylko niektóre niuanse implementacyjne i brak optymalizacji, które są nieistotne dla użytkownika końcowego, być może w punkcie, w którym zaczyna zauważać opóźnienia w wydajności, ale znowu jest to kwestia implementacji, a nie problem architektury.

Oleg Gordeev
źródło
0

dla postów, które wspominają, że python nie musi generować kodu bajtowego, nie jestem pewien, czy to prawda. wydaje się, że wszystkie wywołania w Pythonie muszą mieć .__code__.co_codeatrybut, który zawiera kod bajtowy. Nie widzę sensownego powodu, aby nazywać Pythona „nieskompilowanym” tylko dlatego, że skompilowane artefakty mogą nie zostać zapisane; i często nie są zapisywane przez projekt w Pythonie, na przykład wszystkie kompilacje kompilują nowy kod bajtowy dla swoich danych wejściowych, to jest powód, dla którego zakres zmiennej interpretacji nie jest spójny między compile(mode='exec, ...)i kompiluje, na compile(mode='single', ...)przykład między uruchomieniem skryptu Pythona a użyciem pdb

ThorSummoner
źródło