I'm trying to understand Dart workings, particularly the terminology. I'm currently confused about the Dart runtime and how it differs from the VM.
My understanding is based on Java, where we have the JDK, JRE, and JVM:
JVM (Java Virtual Machine): Loads, verifies, and executes Java bytecode. It's considered the interpreter or the core of Java programming.
JRE (Java Runtime Environment): Needed to run a Java program. It includes the JVM and core libraries but lacks development tools like a compiler.
JDK (Java Development Kit): Contains all tools required for compiling, debugging, and running Java programs.
(Source: IBM Blog on JVM vs JRE vs JDK)
Using this as a basis, I understand the Dart VM is akin to the JVM, translating from an intermediary representation (Dart kernel) to machine code, and capable of JIT and AOT compilation. However, I'm unclear about the exact meaning of 'runtime'.
From Introduction to Dart VM by Mraleph, I understand that the runtime or runtime system is part of the VM. The Dart Overview page states that the runtime manages memory, enforces the Dart type system, and manages isolates. On native platforms, the Dart runtime is included in self-contained executables and is part of the Dart VM provided by the dart run command.
Despite this information, I'm still uncertain about the term 'runtime'. Is it different from the 'runtime environment'? How would you define the runtime system, and how does it differ from the VM, particularly in terms of its specific functions within the VM?
Additionally, there's a discussion on the use of the terms 'Runtime', 'run-time', and 'run time' here, which was both helpful and a bit confusing.
I've tried reading about it on my own but the terms are a bit overloaded
The simple answer in this context I guess would be: a runtime is something that needs to be available while the application is running, and without which the application can't run.
The long answer is, I think, that the most important thing to understand is the execution model, and then perhaps will the runtime be more clear. An application/program can be executed in several ways:
Interpreted means the source code is read by an interpreter and executed directly. In this case the actual text file is read by the interpreter and executed. It is the slowest of them all, and used typically for scripts where performance does not matter that much. You start the application by starting the interpreter and pass the source file to the interpreter. Of course, the source file needs to be in a language that the interpreter understands. For linux scripts the interpreter can be bash, or sh, or other shell variants.
Direct means the application exists as an executable file, which is another way of saying the file contains instructions that your CPU understands, and executing it simply means sending the file content to the CPU (roughly - I'm simplifying a little bit). The application is started by running the executable file, without an interpreter or VM to be around. The downside here is that the executable file can only be executed for the CPU it was compiled for. You can't execute an application compiled for Desktop (Intel CPUs) on a mobile phone. (There's also the topic of the file format, but that's not relevant here: when you have an application compiled for both linux and windows, for Intel CPUs, the executable files will contain the same Intel CPU instructions, only the file format differs).
Through a VM is kind of a combination of the first two: the application exists as an executable file containing CPU instructions (so you are not interpreting source code), but it's not for your CPU, or any CPU actually. It's for a CPU which doesn't exist (hence the "virtual" term), and you need something called a JIT (just-in-time compiler) to be able to run the application. The JIT will translate the instructions from the executable file (for that virtual CPU) to intructions for your actual CPU, while the application is running. So the JIT is always there, doing some extra work next to what the application is actually doing. Common sense tells us that this should be slower than direct execution, but it's actually not the case. There are some advantages. One of them is that the JIT can do some optimisations which the normal compiler can't (the compiler which translated the source code to the executable file), simply because it has access to runtime information. If I were to make an analogy with cooking, AOT would be a recipe which is prepared before and strict, meaning each step must be performed as described, without deviations, and a JIT would be a recipe that is more loose, and can be adjusted on the way, for example we could decide to use less salt, because the herbs which we decided to use (which are optional in the recipe) already contain some salt. So the VM model is more like having an agent that runs alongside your application and can help, and you need to pay the agent as well, while AOT is like trying your best to prepare everything in advance an then just let the beast loose.
Dart can do both VM and AOT, Java can do only do VM (to my knowlegde - I may not be up to date with the latest Java technology).
Note that this is not necessarily related to file formats. Java traditionally used the JAR file format for it's executables, and it is the case that these files can't be executed directly, so you need the Java VM to execute them, so you needed to start the Java VM (like an interpreter) and tell the Java VM what JAR file to execute. It is also possible to create executable files for Java application, but they are basically just the Java VM bundled together with you application, so it's just for convenience as your users shouldn't care or need to know that they need the Java VM. But even though it looks like a direct executable, it's actually just the starting of the Java VM with the instruction to load and execute a JAR file.
In this context, the JRE is the same as the Java VM: the JIT plus some extra stuff.
Dart ist no different: if compiled for the Dart VM, the the executable will contain and/or need the Dart VM to be able to run. But, different than Java, in AOT mode there is no runtime needed, the executable does not need anything else to run, it is self sufficient.
I hope this clarifies it a little bit. I wouldn't get too much fixated about the meaning of runtime in Dart, but rather focus on the the different modes in Dart. Runtime in general just refers to whatever is needed and/or available to the application while it is running.