Presto的核心就是纯内存计算,因此内存对于Presto来说至关重要
    org.airlift是工具类,跟Presto是同一个开发者
    一、Slice

    1. ClassLayout和Unsafe

    在Java中一般是无法取得类的大小的,需要通过一些特殊的手段,例如Unsafe包中的方法
    org.openjdk.jol包封装了很多Unsafe的方法
    我们可以通过jol中的ClassLayout类来或者我们创建的Java对象在内存中的大小
    ① 常规方法

    1. public static void common() {
    2. int []nums = new int[20];
    3. for (int i=0 ; i< nums.length; i++) {
    4. nums[i] = i;
    5. }
    6. for (int i: nums) {
    7. System.out.println(i);
    8. }
    9. }

    ② Unsafe方式

    1. public static void unSafeMethod() throws Exception{
    2. Field field = Unsafe.class.getDeclaredField("theUnsafe");
    3. field.setAccessible(true);
    4. Unsafe unsafe = (Unsafe) field.get(null);
    5. int []nums = new int[20];
    6. for (int i=0 ; i<nums.length; i++) {
    7. unsafe.putInt(nums, (long) i * Unsafe.ARRAY_INT_INDEX_SCALE + Unsafe.ARRAY_INT_BASE_OFFSET, i);
    8. }
    9. System.out.println("BASE_OFFSET:" + Unsafe.ARRAY_INT_BASE_OFFSET);
    10. System.out.println("INDEX_SCALE:" + Unsafe.ARRAY_LONG_INDEX_SCALE);
    11. for (int i: nums) {
    12. System.out.println(i);
    13. }
    14. }

    ③ Unsafe创建大对象的方式(Slice的原理)

    1. public static void unSafeObjectMethod() throws Exception {
    2. Person person = new Person();
    3. ClassLayout personClassLayout = ClassLayout.parseClass(Person.class);
    4. long size = personClassLayout.instanceSize();
    5. System.out.println("size: " + size);
    6. System.out.println("header size: " + personClassLayout.headerSize());
    7. Field field = Unsafe.class.getDeclaredField("theUnsafe");
    8. field.setAccessible(true);
    9. Unsafe unsafe = (Unsafe) field.get(null);
    10. // 得到所有的field信息
    11. SortedSet<FieldLayout> fieldLayouts = personClassLayout.fields();
    12. for (FieldLayout layout: fieldLayouts) {
    13. switch (layout.name()) {
    14. case "name":
    15. unsafe.putObject(person, ((long) layout.offset()), "zhu");
    16. break;
    17. case "age":
    18. unsafe.putInt(person, ((long) layout.offset()), 20);
    19. break;
    20. default:
    21. break;
    22. }
    23. }
    24. System.out.println(person);
    25. }
    26. static class Person {
    27. private String name;
    28. private int age;
    29. }

    2、类详解
    我们无法直接创建Slice类,可以通过Slices类提供的很多的静态方法来进行创建

    1. //利用这个方法我们可以直接创建一个容量为capacity的Slice,底层就是创建了一个byte[capacity]的数组,不过这个对象是在堆内的
    2. Slice allocate(int capacity);
    3. //利用这个方法我们可以在堆外创建一块内存,底层是使用的nio的ByteBuffer.allocateDirect
    4. Slice allocateDirect(int capacity);

    创建了Slice之后,就可以往里面添加元素了
    在Presto中最重要的两个用法就是FixedWidthBlock和VariableWidthBlock
    创建者两种Block运用他的Builder类FixedWidthBlockBuilder和VariableWidthBlockBuilder类
    ① FixedWidthBlock
    定长的Block,所以会固定一个FixedSize,然后底层就是一个byte数组。不管我们往里面写什么,只要一个entry的长度是FixedSize就行。同时这个不提供自动扩展内存的功能,当超出大小时,会抛出异常
    ② VariableWidthBlock
    变长的Block,没有固定的大小,所以需要一个额外的数组记录指定entry的位置同时在每次增加之前会确保内存空间足够,如果不够会进行自动扩容
    二、Block
    三、Page
    小结

    1. 本篇文章主要介绍Slice的实现原理(通过Unsafe的方式),同时介绍Slice的主要应用场景
    2. 由于时间缘故,Block和Page留到下次补充

    参考文献

    1. https://blog.lovezhy.cc/2018/07/19/Presto源码解析-Slice实现/