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
4int[] 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/**
* 调用反射获得Unsafe实例
*/
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/**
* 调用反射获得Unsafe实例
*/
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); // 16
/**
* 把Object写为两个long
*/
unsafe.putLong(object, 0L, 13L);
unsafe.putLong(object, (long) SizeOf.SIZE_OF_LONG, 19L);
System.out.println(unsafe.getLong(object, 0L)); // 13
System.out.println(unsafe.getLong(object, (long)SizeOf.SIZE_OF_LONG)); // 19
System.out.println(object); // upe
需要注意的是,这样Object对象就是不可用了。
如果我们强行输出的话,会抛出空指针异常。
同样的,我们可以对对象进行设值,这个时候我们可以借助ClassLayout封装好的方法1
2
3
4
5
6
7
8
9
10
11
12
13
14static class Person {
private String name;
private int age;
//get set...
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 | Person person = new Person(); |
总之,这就给了一个类似于利用大对象的内存配合Unsafe直接操作内存的方法做一个内存池的思路
Slices
我们无法直接创建Slice类,可以通过Slices类提供的很多的静态方法来进行创建。
1 | Slice allocate(int capacity); |
创建了Slice之后,就可以往里面添加元素了。
在Presto中最重要的两个用法就是FixedWidthBlock和VariableWidthBlock了。
创建者两种Block运用他的Builder类FixedWidthBlockBuilder和VariableWidthBlockBuilder类。
FixedWidthBlockBuilder
定长的Block,所以会固定一个FixedSize,然后底层就是一个byte数组。
不管我们往里面写什么,只要一个entry的长度是FixedSize就行。
同时这个不提供自动扩展内存的功能,当超出大小时,会抛出异常。
VariableWidthBlockBuilder
变长的Block,没有固定的大小,所以需要一个额外的数组记录指定entry的位置
同时在每次增加之前会确保内存空间足够,如果不够会进行自动扩容。