Последнее обновление: 23.11.2023
Автор: Антон Козицкий

JVM, JRE, JDK - что это и зачем, в чем различия

Привет, дорогой мой друг!

В этой статье я хотел бы поговорить с тобой о базовых понятиях в Java-мире. Как ты, возможно, слышал, "Java-программы пишутся единожды - выполняются везде" (по-английски это звучит так "Write once, run anywhere")

Если коротко, то это означает что java-код является переносимым. То есть его можно написать один раз, а потом запускать на самых разных архитектурах и операционных системах. Например,

  • Windows (архитектуры x32, x64)
  • Linux (x64, arm и так далее)
  • MacOS (x64, aarch64)
  • другие операционные системы, о которых мы слышали реже

Если сказать это по другому, то вы можете запускать свой java-код на любой системе, для которой реализована виртуальная машина java. Здесь, вот, мы подошли к первому понятию - JVM (Java Virtual Machine). Это и есть та самая виртуальная машина java, за счет которой достигается переносимость java-кода между платформами.

Чтобы не быть голословным, предлагаю тебе прям сейчас взглянуть на страницу выбора Java для скачивания: Latest Releases | Adoptium

Это страница одного из поставщиков JDK/JRE (другими словами Java). Таких поставщиков есть, как минимум несколько. Но обо всем по порядку. Как ты можешь увидеть, этот поставщик предлагает Java для нескольких операционных систем и для самых разных компьютерных архитектур: 32-битных, 64-битных и так далее.

Итак, что такое JVM

Есть виртуальная машина java, она же JVM (Java Virtual Machine), за счет которой достигается выполнение java-кода (java-программ) на разных операционных системах.

Продолжаем дальше

JVM - это тоже программа, только большая по размеру. Может быть написана, например, на языке С++. Цель этой программы - выполнять java байт-код. Байт-код - это промежуточный код наших java-программ. Давайте посмотрим на примере, чтобы было понятней.

Я написал небольшую программу на Java, которая читает построчно текстовый файл, создает из прочитанных данных HashMap (карту/мапу) и затем выводит этот результат на экран:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Application {

  public static void main(String[] args) {
    Map<String, Integer> goods = new HashMap<>();

    try {
      File goodsFile = new File("goods.txt");
      Scanner reader = new Scanner(goodsFile);

      while (reader.hasNext()) {
        String line = reader.nextLine();
        String[] columns = line.split(":");

        if (columns.length == 2) {
          String good = columns[0].strip();

          try {
            int amount = Integer.parseInt(columns[1].strip());
            goods.put(good, amount);
          } catch (NumberFormatException e) {
            System.out.printf("%s cannot be parsed. Continue...%n", columns[1]);
          }
        }
      }

      reader.close();
    } catch (FileNotFoundException e) {
      System.out.println("goods.txt cannot be found on a disk");
    }

    goods.forEach((key, value) -> System.out.println("Product: " + key + ", amount: " + value));
  }
}
    

Файлик с данными, которые эта программа читает: goods.txt

apple: 200
orange: 150
avocado: 80
banana: 230
    

Как эту программу выполнить?

Любая java-программа проходит два важных этапа:

  1. Компиляция
  2. Выполнение

Компиляция

Компиляция - это, когда мы исходный код программы превращаем в промежуточный код (байт-код). Выше ты мог увидеть исходных код программы. Тот код, который понятен разработчику. Это код, написанный на языке программирования Java.

После компиляции этого кода мы получим байт-код. Он будет выглядеть примерно вот так:

Classfile /home/hp/projects/pet/app/Application.class
  Last modified Mar 4, 2023; size 2566 bytes
  SHA-256 checksum ec326395fb3e756a44d5f2d35a660f9424c8e0445acb7d962bce993f5bc00277
  Compiled from "Application.java"
public class Application
  minor version: 0
  major version: 61
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #95                         // Application
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 3
Constant pool:
    #1 = Methodref          #2.#3         // java/lang/Object."<init>":()V
    #2 = Class              #4            // java/lang/Object
    #3 = NameAndType        #5:#6         // "<init>":()V
    #4 = Utf8               java/lang/Object
    #5 = Utf8               <init>
    #6 = Utf8               ()V
    #7 = Class              #8            // java/util/HashMap
    #8 = Utf8               java/util/HashMap
    #9 = Methodref          #7.#3         // java/util/HashMap."<init>":()V
   #10 = Class              #11           // java/io/File
   #11 = Utf8               java/io/File
   #12 = String             #13           // goods.txt
   #13 = Utf8               goods.txt
   #14 = Methodref          #10.#15       // java/io/File."<init>":(Ljava/lang/String;)V
   #15 = NameAndType        #5:#16        // "<init>":(Ljava/lang/String;)V
   #16 = Utf8               (Ljava/lang/String;)V
   #17 = Class              #18           // java/util/Scanner
   #18 = Utf8               java/util/Scanner
   #19 = Methodref          #17.#20       // java/util/Scanner."<init>":(Ljava/io/File;)V
   #20 = NameAndType        #5:#21        // "<init>":(Ljava/io/File;)V
   #21 = Utf8               (Ljava/io/File;)V
   #22 = Methodref          #17.#23       // java/util/Scanner.hasNext:()Z
   #23 = NameAndType        #24:#25       // hasNext:()Z
   #24 = Utf8               hasNext
   #25 = Utf8               ()Z
   #26 = Methodref          #17.#27       // java/util/Scanner.nextLine:()Ljava/lang/String;
   #27 = NameAndType        #28:#29       // nextLine:()Ljava/lang/String;
   #28 = Utf8               nextLine
   #29 = Utf8               ()Ljava/lang/String;
   #30 = String             #31           // :
   #31 = Utf8               :
   #32 = Methodref          #33.#34       // java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
   #33 = Class              #35           // java/lang/String
   #34 = NameAndType        #36:#37       // split:(Ljava/lang/String;)[Ljava/lang/String;
   #35 = Utf8               java/lang/String
   #36 = Utf8               split
   #37 = Utf8               (Ljava/lang/String;)[Ljava/lang/String;
   #38 = Methodref          #33.#39       // java/lang/String.strip:()Ljava/lang/String;
   #39 = NameAndType        #40:#29       // strip:()Ljava/lang/String;
   #40 = Utf8               strip
   #41 = Methodref          #42.#43       // java/lang/Integer.parseInt:(Ljava/lang/String;)I
   #42 = Class              #44           // java/lang/Integer
   #43 = NameAndType        #45:#46       // parseInt:(Ljava/lang/String;)I
   #44 = Utf8               java/lang/Integer
   #45 = Utf8               parseInt
   #46 = Utf8               (Ljava/lang/String;)I
   #47 = Methodref          #42.#48       // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #48 = NameAndType        #49:#50       // valueOf:(I)Ljava/lang/Integer;
   #49 = Utf8               valueOf
   #50 = Utf8               (I)Ljava/lang/Integer;
   #51 = InterfaceMethodref #52.#53       // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
   #52 = Class              #54           // java/util/Map
   #53 = NameAndType        #55:#56       // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
   #54 = Utf8               java/util/Map
   #55 = Utf8               put
   #56 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
   #57 = Class              #58           // java/lang/NumberFormatException
   #58 = Utf8               java/lang/NumberFormatException
   #59 = Fieldref           #60.#61       // java/lang/System.out:Ljava/io/PrintStream;
   #60 = Class              #62           // java/lang/System
   #61 = NameAndType        #63:#64       // out:Ljava/io/PrintStream;
   #62 = Utf8               java/lang/System
   #63 = Utf8               out
   #64 = Utf8               Ljava/io/PrintStream;
   #65 = String             #66           // %s cannot be parsed. Continue...%n
   #66 = Utf8               %s cannot be parsed. Continue...%n
   #67 = Methodref          #68.#69       // java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   #68 = Class              #70           // java/io/PrintStream
   #69 = NameAndType        #71:#72       // printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   #70 = Utf8               java/io/PrintStream
   #71 = Utf8               printf
   #72 = Utf8               (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   #73 = Methodref          #17.#74       // java/util/Scanner.close:()V
   #74 = NameAndType        #75:#6        // close:()V
   #75 = Utf8               close
   #76 = Class              #77           // java/io/FileNotFoundException
   #77 = Utf8               java/io/FileNotFoundException
   #78 = String             #79           // goods.txt cannot be found on a disk
   #79 = Utf8               goods.txt cannot be found on a disk
   #80 = Methodref          #68.#81       // java/io/PrintStream.println:(Ljava/lang/String;)V
   #81 = NameAndType        #82:#16       // println:(Ljava/lang/String;)V
   #82 = Utf8               println
   #83 = InvokeDynamic      #0:#84        // #0:accept:()Ljava/util/function/BiConsumer;
   #84 = NameAndType        #85:#86       // accept:()Ljava/util/function/BiConsumer;
   #85 = Utf8               accept
   #86 = Utf8               ()Ljava/util/function/BiConsumer;
   #87 = InterfaceMethodref #52.#88       // java/util/Map.forEach:(Ljava/util/function/BiConsumer;)V
   #88 = NameAndType        #89:#90       // forEach:(Ljava/util/function/BiConsumer;)V
   #89 = Utf8               forEach
   #90 = Utf8               (Ljava/util/function/BiConsumer;)V
   #91 = InvokeDynamic      #1:#92        // #1:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
   #92 = NameAndType        #93:#94       // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
   #93 = Utf8               makeConcatWithConstants
   #94 = Utf8               (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
   #95 = Class              #96           // Application
   #96 = Utf8               Application
   #97 = Utf8               Code
   #98 = Utf8               LineNumberTable
   #99 = Utf8               main
  #100 = Utf8               ([Ljava/lang/String;)V
  #101 = Utf8               StackMapTable
  #102 = Class              #103          // "[Ljava/lang/String;"
  #103 = Utf8               [Ljava/lang/String;
  #104 = Utf8               lambda$main$0
  #105 = Utf8               (Ljava/lang/String;Ljava/lang/Integer;)V
  #106 = Utf8               SourceFile
  #107 = Utf8               Application.java
  #108 = Utf8               BootstrapMethods
  #109 = MethodHandle       6:#110        // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #110 = Methodref          #111.#112     // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #111 = Class              #113          // java/lang/invoke/LambdaMetafactory
  #112 = NameAndType        #114:#115     // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #113 = Utf8               java/lang/invoke/LambdaMetafactory
  #114 = Utf8               metafactory
  #115 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #116 = MethodType         #117          //  (Ljava/lang/Object;Ljava/lang/Object;)V
  #117 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)V
  #118 = MethodHandle       6:#119        // REF_invokeStatic Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
  #119 = Methodref          #95.#120      // Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
  #120 = NameAndType        #104:#105     // lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
  #121 = MethodType         #105          //  (Ljava/lang/String;Ljava/lang/Integer;)V
  #122 = MethodHandle       6:#123        // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #123 = Methodref          #124.#125     // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #124 = Class              #126          // java/lang/invoke/StringConcatFactory
  #125 = NameAndType        #93:#127      // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #126 = Utf8               java/lang/invoke/StringConcatFactory
  #127 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #128 = String             #129          // Product: \u0001, amount: \u0001
  #129 = Utf8               Product: \u0001, amount: \u0001
  #130 = Utf8               InnerClasses
  #131 = Class              #132          // java/lang/invoke/MethodHandles$Lookup
  #132 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #133 = Class              #134          // java/lang/invoke/MethodHandles
  #134 = Utf8               java/lang/invoke/MethodHandles
  #135 = Utf8               Lookup
{
  public Application();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=7, locals=8, args_size=1
         0: new           #7                  // class java/util/HashMap
         3: dup
         4: invokespecial #9                  // Method java/util/HashMap."<init>":()V
         7: astore_1
         8: new           #10                 // class java/io/File
        11: dup
        12: ldc           #12                 // String goods.txt
        14: invokespecial #14                 // Method java/io/File."<init>":(Ljava/lang/String;)V
        17: astore_2
        18: new           #17                 // class java/util/Scanner
        21: dup
        22: aload_2
        23: invokespecial #19                 // Method java/util/Scanner."<init>":(Ljava/io/File;)V
        26: astore_3
        27: aload_3
        28: invokevirtual #22                 // Method java/util/Scanner.hasNext:()Z
        31: ifeq          119
        34: aload_3
        35: invokevirtual #26                 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
        38: astore        4
        40: aload         4
        42: ldc           #30                 // String :
        44: invokevirtual #32                 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
        47: astore        5
        49: aload         5
        51: arraylength
        52: iconst_2
        53: if_icmpne     116
        56: aload         5
        58: iconst_0
        59: aaload
        60: invokevirtual #38                 // Method java/lang/String.strip:()Ljava/lang/String;
        63: astore        6
        65: aload         5
        67: iconst_1
        68: aaload
        69: invokevirtual #38                 // Method java/lang/String.strip:()Ljava/lang/String;
        72: invokestatic  #41                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
        75: istore        7
        77: aload_1
        78: aload         6
        80: iload         7
        82: invokestatic  #47                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        85: invokeinterface #51,  3           // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
        90: pop
        91: goto          116
        94: astore        7
        96: getstatic     #59                 // Field java/lang/System.out:Ljava/io/PrintStream;
        99: ldc           #65                 // String %s cannot be parsed. Continue...%n
       101: iconst_1
       102: anewarray     #2                  // class java/lang/Object
       105: dup
       106: iconst_0
       107: aload         5
       109: iconst_1
       110: aaload
       111: aastore
       112: invokevirtual #67                 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
       115: pop
       116: goto          27
       119: aload_3
       120: invokevirtual #73                 // Method java/util/Scanner.close:()V
       123: goto          135
       126: astore_2
       127: getstatic     #59                 // Field java/lang/System.out:Ljava/io/PrintStream;
       130: ldc           #78                 // String goods.txt cannot be found on a disk
       132: invokevirtual #80                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       135: aload_1
       136: invokedynamic #83,  0             // InvokeDynamic #0:accept:()Ljava/util/function/BiConsumer;
       141: invokeinterface #87,  2           // InterfaceMethod java/util/Map.forEach:(Ljava/util/function/BiConsumer;)V
       146: return
      Exception table:
         from    to  target type
            65    91    94   Class java/lang/NumberFormatException
             8   123   126   Class java/io/FileNotFoundException
      LineNumberTable:
        line 10: 0
        line 13: 8
        line 14: 18
        line 16: 27
        line 17: 34
        line 18: 40
        line 20: 49
        line 21: 56
        line 24: 65
        line 25: 77
        line 28: 91
        line 26: 94
        line 27: 96
        line 30: 116
        line 32: 119
        line 35: 123
        line 33: 126
        line 34: 127
        line 37: 135
        line 38: 146
      StackMapTable: number_of_entries = 6
        frame_type = 254 /* append */
          offset_delta = 27
          locals = [ class java/util/Map, class java/io/File, class java/util/Scanner ]
        frame_type = 255 /* full_frame */
          offset_delta = 66
          locals = [ class "[Ljava/lang/String;", class java/util/Map, class java/io/File, class java/util/Scanner, class java/lang/String, class "[Ljava/lang/String;", class java/lang/String ]
          stack = [ class java/lang/NumberFormatException ]
        frame_type = 248 /* chop */
          offset_delta = 21
        frame_type = 2 /* same */
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class "[Ljava/lang/String;", class java/util/Map ]
          stack = [ class java/io/FileNotFoundException ]
        frame_type = 8 /* same */
}
SourceFile: "Application.java"
BootstrapMethods:
  0: #109 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #116 (Ljava/lang/Object;Ljava/lang/Object;)V
      #118 REF_invokeStatic Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
      #121 (Ljava/lang/String;Ljava/lang/Integer;)V
  1: #122 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #128 Product: \u0001, amount: \u0001
InnerClasses:
  public static final #135= #131 of #133; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    

Этот код понятен лишь виртуальной машине java (JVM).

Выполнение

Байт-код у нас есть. Теперь этот код можно выполнить, но есть один очень важный момент: в дополнении к JVM еще идут java-библиотеки. В этих библиотеках находится реализация java-коллекций для работы с данными, классы для работы с потоками ввода/вывода и еще много чего полезного и необходимого для работы.

Так вот JVM + библиотеки = JRE (Java Runtime Environment).

JRE - это среда выполнения java. Для того чтобы запустить java-программу нам нужна именно JRE. Это минимальный набор для запуска и работы любой java-программы.

Но как быть, если вам нужно заниматься разработкой на java? Здесь уже не обойтись без JDK (Java Development Kit).

JDK - это полноценный набор для разработчика на java. В составе JDK ты найдешь:

  1. компилятор - чтобы получить байт-код из исходного кода своей программы;
  2. отладчик - чтобы находить причины ошибок в своем коде;
  3. инструмент для чтения байт-кода (если тебе это будет для чего-то нужно);
  4. генератор документации для твоих классов и их методов;
  5. и еще много чего полезного и интересного.

И в самом конце я представляю вашему вниманию полную обзорную картину, показывающую взаимосвязь между JVM, JRE и JDK:

Вместо заключения

Java - это:

  • и язык программирования;
  • и название платформы (по аналогии с .NET для C#).

В этой статье мы поговорили о платформе Java и о взаимосвязи между ее компонентами, такими как JVM, JRE, JDK. Напомню, что платформа Java - это программное обеспечение, дающее нам возможность разрабатывать и затем запускать java-программы.

Если говорить про языки программирования, то ты можешь писать не только на Java, но и на Scala, Kotlin, Groovy, Clojure. Это языки программирования, которые тоже совместимы с платформой Java. Совместимы - это значит, что они тоже компилируются в байт-код, который может выполнять JVM.