Valhalla Value Type

Background

Valhalla于2014年开始,项目的目标是提供一个flattened type,也叫inline class(代码里面用得多,关键字也叫这个), value type(编程语言共享的名字,比如很多语言都有继承这个概念,但是具体表现形式不一样),primitive class(口号是Codes like a class, works like an int,突出这些类的行为和primitive type类似)。什么是flatten呢,比如一个Point[]现在长这样:
image

flatten之后
image

在JVM出现的20实际90年代,算术运算和内存内存读取的成本差距不大,但是现在有了memory cache,指令级并行,cache miss的成本相当于1000次算术运输了,以前对象的间接布局与现代的处理器不太匹配了。

Valhalla的keyword变化非常迅速,极其不稳定,目前已知已经变过:__ByValue, value, inline, primitive

//TODO: 如果Point{}里面有Object,他的header会去掉吗?Point会变成flatten吗?对Point[3].hashCode()怎么办?

新字节码

defaultvalue

public primitive class Point{
    int x;
    int y;
}
Point p = Point.default;
Point p2 = new Point(0,0);

Point.default对象其实是Point.class上隐含的一个字段".default"

class InstanceKlass: public Klass{
   ...   
  int default_value_offset() {
    int offset = *((int*)adr_default_value_offset());
    assert(offset != 0, "must not be called if not initialized");
    return offset;
  }

  void set_default_value(oop val) {
    java_mirror()->obj_field_put(default_value_offset(), val);
  }

  oop default_value() {
    oop val = java_mirror()->obj_field_acquire(default_value_offset());
    assert(oopDesc::is_oop(val), "Sanity check");
    assert(val->is_inline_type(), "Sanity check");
    assert(val->klass() == this, "sanity check");
    return val;
  }
};

withfield

对primitive的状态修改只能通过withfield字节码进行。

__WithField只能在inline类内部使用,而且修改的对象仅限于当前类,比如

public inline class Point {
    public int x;
    public int y;
    public static Point makePoint(int x, int y) {
        Point p = new Point(x, y);
        p = __WithField(p.x,0);
        return p;
    }

    Point() {
        this.x = 100000303;
        this.y = 202342423;
    }
}

是可以的,但是其他地方就不行:

public inline class Test{
    static Point p = Point.makePoint(17,5);

    static void test1(Point px){
        Point p = Point.default;
        p = __WithField(p.x,4634); // 编译错误
        b = x;
        ...
    }

只是个人观察是这样,实际可能还有更通用更准确的定义。

Q-Type

对象布局

primitive类型的Line,里面包含两个primitive类型的Point

public primitive class Point {
    public long x;
    public long y;
    ....
 }
public final primitive class Line {
    public Point p1;
    public Point p2;
    ....
}

用PrintInlineLayout可以输出它的layout

// -XX:-UseCompressedOops -XX:-UseCompressedClassPointer
Layout of class Line
Instance fields:
 @0 16/- RESERVED
 @16 "p1" QPoint; 16/8 INLINED
 @32 "p2" QPoint; 16/8 INLINED
Static fields:
 @0 192/- RESERVED
 @192 ".default" Ljava/lang/Object; 8/8 REGULAR
Instance size = 48 bytes
First field offset = 16
Alignment = 8 bytes
Exact size = 32 bytes

//-XX:-UseCompressedOops -XX:+UseCompressedClassPointer
Layout of class Line
Instance fields:
 @0 12/- RESERVED
 @12 4/1 PADDING
 @16 "p1" QPoint; 16/8 INLINED
 @32 "p2" QPoint; 16/8 INLINED
Static fields:
 @0 184/- RESERVED
 @184 ".default" Ljava/lang/Object; 8/8 REGULAR
Instance size = 48 bytes
First field offset = 16
Alignment = 8 bytes
Exact size = 32 bytes

//-XX:+UseCompressedOops -XX:+UseCompressedClassPointer (default)
Layout of class Line
Instance fields:
 @0 12/- RESERVED
 @12 4/1 PADDING
 @16 "p1" QPoint; 16/8 INLINED
 @32 "p2" QPoint; 16/8 INLINED
Static fields:
 @0 112/- RESERVED
 @112 ".default" Ljava/lang/Object; 4/4 REGULAR
Instance size = 48 bytes
First field offset = 16
Alignment = 8 bytes
Exact size = 32 bytes

primtive type还有一个特性是它会对field进行排序,让内存占用最小,因为primitive type的设计意图就是让它能嵌入进其他容器里面,所以越小越好。考虑下面的类

public final primitive class Line {
    public Point p1;
    public char cc;
    public Point p2;
    ...
}

vm会将cc放到最后面,而不是按照program order放中间:

Layout of class Line
Instance fields:
 @0 12/- RESERVED
 @12 4/1 PADDING
 @16 "p1" QPoint; 16/8 INLINED
 @32 "p2" QPoint; 16/8 INLINED
 @48 "cc" C 2/2 REGULAR
Static fields:
 @0 112/- RESERVED
 @112 ".default" Ljava/lang/Object; 4/4 REGULAR
Instance size = 56 bytes
First field offset = 16
Alignment = 8 bytes
Exact size = 34 bytes

排序策略是:1. 先放到的primitive field 2.然后oop field 3.最后小的primitive field

Type.ref

XX.ref可以一句话概括为:nullable的primitive type

默认的值类型都是不能为null的,比如:

primitive class Point{
    int x;
    int y;
} 

Point[] p = new Point[5]
p.x += p.y;

虽然只分配了p数组,里面没有new Point(),但是p.x还是能正常工作,因为vm会为他默认创建值,上面的代码相当于:

Point[] p = new Point[5]
for (int i=0;i<5;i++){
  p = Point.default;
}
p.x += p.y;

但是有些场合要有可以为null的primitive type,比如用容器存放primitive type,这个时候就可以这样写:

List<Point.ref> p = new ArrayList<>();
p.add(null);
p.add(Point.default);

假如用户写了一个primitive类型的Point.java,javac会自动生成一个companion类,即Point$ref.class。这个类就是可null的。

Point p1 = null;     // ERROR, javac错误,不兼容类型
Point.ref p2 = null; // OK

Identity Object

参见最初的提案http://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html
inline class的instance没有identity,也就是说,不能将inline instance用于一些identity-sensitive的操作(比如synchronization,exception)。为了避免混乱,也为了便于描述,valhalla引入了identity class的概念,identity class的instance就叫做identity object。

幼虫态

larval state

调用约定

InlineTypePassFieldsAsArgs

ScalarizeInlineTypes

UseArrayMarkWordCheck

ForceNonTearable

ProfileACmpTypes

原文地址:https://www.cnblogs.com/kelthuzadx/p/15726480.html