View on GitHub


IJVM is an instruction set architecture that is mostly a subset of the JVM assembly language used in the Java platform.

IJVM Project

1. Summary

2. Skeleton

IJVM interface functions

  1. machine.c
  2. ijvm.c && ijvm.h

IJVM structure and Initial functions

IJVM ISA implementation

Stack structure and operations

Heap structure and operations

Garbage collection functions

Network communication and multiple connection


Snapshots functions

Utility functions

3. Implementation

(1) Stack machine:

            |        ......        |
            |operation stack above | [...]<- sp
            |        old_lv        |
            |        old_pc        |
            |        var[2]        |
            |        var[1]        |
            |        var[0]        |
            |        ......        |
            |        arg[2]        |
            |        arg[1]        |
            |        arg[0]        |
            |        link_ptr      | [...]<- lv
            |        ......        | [2]
            |        ......        | [1]
            |       MAIN_FRAM      | [0]

(2) Heap memory

table ——>| Cell | 
         | Cell |
         | Cell | ——> arrary ——>|[0]|[1]|[2]|... + |mark bit|
         | Cell |
         | Cell |
array reference table ——> |[0]|[1]|[2]|...

(3) Garbage Collection

I implement two GC mechanisms but the last does not work in our PAD context.

  1. Mark and Sweep: This mechanism will be triggered when the heap is full. It can also be triggered by the opcode OP_GC (0xD4)
    - Mark phase:
       GC traverses the stack and marks every possible alive heap Cell.
    - Sweep phase:
            GC traverses the heap and frees all dead Cells.
    - Compact phase:
        I did not implement the memory compaction because I use NextFreeCell() to prevent memory fragmentation; NextFreeCell() will find the next free cell and the new array will be built in this location. In other words, I will reuse the freed location (free list) first.
  2. Track each array reference This mechanism will be triggered when a stack frame is destroyed.

    Firstly, I built a tracking table when a new array was allocated. When a stack frame is destroyed, I check the tracking table and free all the array allocated by this frame (except the array that returns by this frame).

(4) GUI

I implement GUI using GTK2.0 rather that Nuklear. Dependency:

sudo apt-get install gtk2.0
sudo apt-get install gimp
sudo apt install libcanberra-gtk-module libcanberra-gtk3-module
sudo apt-get install libatk-adaptor libgail-common


make ijvm-gui

(5) Network communication

  1. I use the normal socket API and TCP protocol.
  2. The opcode NETBIND (0xE1) will start the localhost server with the loopback address.

(6) Network multiple connection

  1. I use the netref argument as an identifier for the network connection and place the netref on the stack when a new connection is set up.
  2. I use the netref as an identifier to build a communication table for each client. When the opcode NETIN (0xE3) is triggered, the received data will be recorded in the corresponding communication table.

(7) Snapshots

When SIGINT is captured by the signal handler, the current IJVM instance will be saved in a file called ijvm.config.

Memory compaction:

I use 2 methods to compact the saved ijvm.config file:

  1. Instead of saving the entire main frame local variable array which has 2^16 words, I count the variable number when loading the program and only save these variable to the ijvm.config file.
  2. I discard the whole original program and only keep parsed text and constant pool sections. Usage:
    ./ijvm -r binary


    ./ijvm -resume binary

4. Compiling

Requires make and GCC or Clang

Run make ijvm to build the ijvm binary

You can enable the debug print (dprintf) found in include/util.h by setting the -DDEBUG compiler flag (e.g., make clean && make testbasic CFLAGS=-DDEBUG).

5. Running a binary

Run an IJVM program using ./ijvm binary. For example ./ijvm files/advanced/Tanenbaum.ijvm.

6. Adding header files

Add your header files to the folder include.

7. Testing

To run a specific test run make run_testX (e.g. make run_test1).

You can debug the tests by running the binaries generated by make build_tests through GDB.

8. Make gzipped tarball

Generate a gzipped tarball of your project using the make dist command. Make sure to double check that all your required files are included in the tarball.

9. Compatibility

You need a valid C11 compiler, such as clang or gcc, as well as glibc. Do not use any non-standard libraries.

10. Windows

To develop on Windows, we recommend using the Windows Subsystem for Linux, and installing Ubuntu from the Microsoft Store. Once you have gone through the setup steps, you can access the Linux command line by executing bash from the Windows command line. You can access your files in the Windows file system through mount points (e.g. cd /mnt/c/Users/<username>/Desktop).

Both glibc, gcc, and make are included in the package build-essential. To install this package, execute the following commands:

Now you can compile the project by navigating to this directory and executing the make command.

11. Tools

You can install the goJASM assembler by executing make tools. This will download a goJASM executable in the tools directory.