вівторок, 8 березня 2011 р.

Как выглядит счетчик в байт-коде?

Начав глубже копать многопоточность, захотел разобраться с байт-кодом и общими принципами работы JVM. Эта заметка дает общее представление о том, что такое байт-код и что он из себя представляет, и продемонстрирует неатомарность операции инкремента.

Что такое байт-код?

Байт-код - это инструкции интерпретатору Java-машины, своего рода ассемблерный язык.
Рассмотрим простой класс - счетчик, значение которого можно увеличить или уменьшить.

// Counter.java
public class Counter {
    private int counter = 0;

    public int inc() {
        return ++counter;
    };

    public int dec() {
        return --counter;
    }

    public int getValue() {
        return counter;
    }

    public static void main(String[] args) {
        Counter c = new Counter();
        System.out.println(c.inc());
    }
}

Скомпилировав его в Counter.class, мы можем теперь декомпилировать класс в байт-код с помощью утилиты javap

grim@blackbox:~/projects/privy/trunk/scl/sourcepath$ javap -c Counter

Compiled from "Counter.java"
public class Counter extends java.lang.Object{
public Counter();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: aload_0
   5: iconst_0
   6: putfield #2; //Field counter:I
   9: return

public int inc();
  Code:
   0: aload_0
   1: dup
   2: getfield #2; //Field counter:I
   5: iconst_1
   6: iadd
   7: dup_x1
   8: putfield #2; //Field counter:I
   11: ireturn

public int dec();
  Code:
   0: aload_0
   1: dup
   2: getfield #2; //Field counter:I
   5: iconst_1
   6: isub
   7: dup_x1
   8: putfield #2; //Field counter:I
   11: ireturn

public int getValue();
  Code:
   0: aload_0
   1: getfield #2; //Field counter:I
   4: ireturn

public static void main(java.lang.String[]);
  Code:
   0: new #3; //class Counter
   3: dup
   4: invokespecial #4; //Method "<init>":()V
   7: astore_1
   8: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   11: aload_1
   12: invokevirtual #6; //Method inc:()I
   15: invokevirtual #7; //Method java/io/PrintStream.println:(I)V
   18: return

}

Байт-код как мы видим - это поток инструкций JVM. Каждая инструкция состоит из кода операции и опционально от одного до нескольких операндов.

Дальше приведу краткий разбор метода inc():

0 aload_0                              ; загружаем ссылку на экземпляр обьекта из переменной и толкаем ее в стек
 1 dup                                  ; копируем верхний элемент стека и толкаем его в стек
 2 getfield #2 <counter.counter>        ; извлекаем из стека ссылку, получаем у нее поле counter (в пуле констант имеет номер #2), и толкаем это значение в стек
 5 iconst_1                             ; толкаем в стек константу-единицу
 6 iadd                                 ; извлекаем два верхних элемента стека, выполняем сложение, результат толкаем в стек
 7 dup_x1                               ; копируем верхний элемент стека и размещаем его перед вторым элементом в стеке
 8 putfield #2 <counter.counter>        ; извлекаем из стека последовательно значение и ссылку на экземпляр, устанавливаем новое значение поля counter
11 ireturn                              ; извлекаем из стека значение и интерпретатор передает контроль вызывающему методу.


Как видно одна операция инкремента скомпилировалась в целых 6 инструкций (со 2 по 8). Это и есть пресловутая неатомарность операции. Один из последствий неатомарности при вызове метода из нескольких потоков возможно несогласованное чтение одного и того же значения, что приведет к возврату одного и того же значения обоими потоками, что противоречит контракту inc().

На этом завершаю, ибо нашел замечательную статью Антона Архипова Java Bytecode Fundamentals (на английском языке). Лучше, чем он, пожалуй, про байт-код расписать не смогу.

Ссылки

Bytecode Basics A First Look at the Bytecodes of the Java Virtual Machine by Bill Venners

The JavaTM Virtual Machine Specification Second Edition Tim Lindholm Frank Yellin

Java Bytecode Fundamentals by Anton Arhipov

2 коментарі:

  1. "Лучше, чем он, пожалуй, про байт-код расписать не смогу."

    спасибо, польстило :)

    ВідповістиВидалити
  2. Вот может и продолжение понравится :)

    http://www.zeroturnaround.com/blog/java-bytecode-fundamentals-using-objects-and-calling-methods/

    ВідповістиВидалити