Presto的核心就是纯内存计算,因此内存对于Presto来说至关重要
org.airlift是工具类,跟Presto是同一个开发者
一、Slice
- ClassLayout和Unsafe
在Java中一般是无法取得类的大小的,需要通过一些特殊的手段,例如Unsafe包中的方法
org.openjdk.jol包封装了很多Unsafe的方法
我们可以通过jol中的ClassLayout类来或者我们创建的Java对象在内存中的大小
① 常规方法
public static void common() {
int []nums = new int[20];
for (int i=0 ; i< nums.length; i++) {
nums[i] = i;
}
for (int i: nums) {
System.out.println(i);
}
}
② Unsafe方式
public static void unSafeMethod() throws Exception{
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);
}
System.out.println("BASE_OFFSET:" + Unsafe.ARRAY_INT_BASE_OFFSET);
System.out.println("INDEX_SCALE:" + Unsafe.ARRAY_LONG_INDEX_SCALE);
for (int i: nums) {
System.out.println(i);
}
}
③ Unsafe创建大对象的方式(Slice的原理)
public static void unSafeObjectMethod() throws Exception {
Person person = new Person();
ClassLayout personClassLayout = ClassLayout.parseClass(Person.class);
long size = personClassLayout.instanceSize();
System.out.println("size: " + size);
System.out.println("header size: " + personClassLayout.headerSize());
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
// 得到所有的field信息
SortedSet<FieldLayout> fieldLayouts = personClassLayout.fields();
for (FieldLayout layout: fieldLayouts) {
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);
}
static class Person {
private String name;
private int age;
}
2、类详解
我们无法直接创建Slice类,可以通过Slices类提供的很多的静态方法来进行创建
//利用这个方法我们可以直接创建一个容量为capacity的Slice,底层就是创建了一个byte[capacity]的数组,不过这个对象是在堆内的
Slice allocate(int capacity);
//利用这个方法我们可以在堆外创建一块内存,底层是使用的nio的ByteBuffer.allocateDirect
Slice allocateDirect(int capacity);
创建了Slice之后,就可以往里面添加元素了
在Presto中最重要的两个用法就是FixedWidthBlock和VariableWidthBlock
创建者两种Block运用他的Builder类FixedWidthBlockBuilder和VariableWidthBlockBuilder类
① FixedWidthBlock
定长的Block,所以会固定一个FixedSize,然后底层就是一个byte数组。不管我们往里面写什么,只要一个entry的长度是FixedSize就行。同时这个不提供自动扩展内存的功能,当超出大小时,会抛出异常
② VariableWidthBlock
变长的Block,没有固定的大小,所以需要一个额外的数组记录指定entry的位置同时在每次增加之前会确保内存空间足够,如果不够会进行自动扩容
二、Block
三、Page
小结
- 本篇文章主要介绍Slice的实现原理(通过Unsafe的方式),同时介绍Slice的主要应用场景
- 由于时间缘故,Block和Page留到下次补充
参考文献