zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    [每日一题]石子合并 -- 区间DP
    [每日一题]: 最长上升子序列 AND 最长不上升子序列
    [每日一题]:最大子矩阵和
    入门DP--最大子段和
    [转载]:正确的提问方式
    springmvc.xml
    service层springservice.xml文件
    aop切面配置
    配置事务通知
    短信验证
  • 原文地址:https://www.cnblogs.com/kelthuzadx/p/15726480.html
Copyright © 2011-2022 走看看