2020-11-21日更:
x信金科,如果大数据部招人,如果你是业务出生/学历不好/公司背景不强,就不要浪费他们的时间了
前言
Presto的Slice并不在Presto包中,是在一个独立的包org.airlift.slice
中
org.airlift
是个工具类,作者也是Presto的主要开发者,主要是服务于Presto的,但是我们也可以单独取出来用。
ClassLayout和Unsafe
在Java中一般是无法取得类的大小的,需要通过一些特殊的手段,例如Unsafe包中的方法。
org.openjdk.jol
包封装了很多Unsafe的方法。
我们可以通过ClassLayout
类来或者我们创建的Java对象在内存中的大小。
通过Unsafe我们可以对对象的内存直接进行操作。
假设我们创建一个int类型的数组,每个元素的值是他Index的位置
1 2 3 4
| int[] nums = new int[20]; for (int i = 0; i < nums.length; i++) { nums[i] = i; }
|
上面这种写法是常规的写法。
我们也可以用Unsafe。
1 2 3 4 5 6 7 8 9 10 11
|
Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null);
int[] nums = new int[20]; for (int i = 0; i < nums.length; i++) { unsafe.putInt(nums, (long) i * Unsafe.ARRAY_INT_INDEX_SCALE + Unsafe.ARRAY_INT_BASE_OFFSET, i); }
|
Unsafe实例我们需要通过反射获得,直接获得会抛出异常。
ARRAY_INT_BASE_OFFSET表示数组对象的第一个元素在内存中的位置。
ARRAY_INT_INDEX_SCALE表示每一个真正的元素的数据中的占据空间。
这些变量都在Unsafe中,还有许多,对应byte数组,long数组等。
同样的,只要能获得对象的地址,那么我们就可以对任意的对象进行写入。
我们尝试在Object对象中写入两个Long元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null);
Object object = new Object(); System.out.println(object); int size = ClassLayout.parseClass(Object.class).instanceSize(); System.out.println("size: " + size);
unsafe.putLong(object, 0L, 13L); unsafe.putLong(object, (long) SizeOf.SIZE_OF_LONG, 19L);
System.out.println(unsafe.getLong(object, 0L)); System.out.println(unsafe.getLong(object, (long)SizeOf.SIZE_OF_LONG));
System.out.println(object);
|
需要注意的是,这样Object对象就是不可用了。
如果我们强行输出的话,会抛出空指针异常。
同样的,我们可以对对象进行设值,这个时候我们可以借助ClassLayout封装好的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static class Person { private String name; private int age;
@Override public String toString() { final StringBuilder sb = new StringBuilder("Person{"); sb.append("name='").append(name).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| Person person = new Person(); ClassLayout personClassLayout = ClassLayout.parseClass(Person.class); int size = personClassLayout.instanceSize(); System.out.println("size: " + size); System.out.println("header size: " + personClassLayout.headerSize());
SortedSet<FieldLayout> fields = personClassLayout.fields(); for (FieldLayout layout : fields) { switch (layout.name()) { case "name": unsafe.putObject(person, ((long) layout.offset()), "Zhu"); break; case "age": unsafe.putInt(person, ((long) layout.offset()), 20); break; default: break; } } System.out.println(person);
|
总之,这就给了一个类似于利用大对象的内存配合Unsafe直接操作内存的方法做一个内存池的思路
Slices
我们无法直接创建Slice类,可以通过Slices类提供的很多的静态方法来进行创建。
1 2 3 4
| Slice allocate(int capacity);
Slice allocateDirect(int capacity);
|
创建了Slice之后,就可以往里面添加元素了。
在Presto中最重要的两个用法就是FixedWidthBlock和VariableWidthBlock了。
创建者两种Block运用他的Builder类FixedWidthBlockBuilder和VariableWidthBlockBuilder类。
FixedWidthBlockBuilder

定长的Block,所以会固定一个FixedSize,然后底层就是一个byte数组。
不管我们往里面写什么,只要一个entry的长度是FixedSize就行。
同时这个不提供自动扩展内存的功能,当超出大小时,会抛出异常。
VariableWidthBlockBuilder

变长的Block,没有固定的大小,所以需要一个额外的数组记录指定entry的位置
同时在每次增加之前会确保内存空间足够,如果不够会进行自动扩容。
Gitalking ...