Julia对象的内存布局
对象布局(jl_value_t)
该 jl_value_t struct是Julia垃圾回收器拥有的内存块的名称,表示与内存中的Julia对象关联的数据。 没有任何类型信息,它只是一个不透明的指针:
typedef struct jl_value_t* jl_pvalue_t;
每个 jl_value_t struct包含在一个 jl_typetag_t 包含有关Julia对象的元数据信息的结构,例如其类型和垃圾回收器(gc)可达性:
typedef struct {
opaque metadata;
jl_value_t value;
} jl_typetag_t;
任何Julia对象的类型都是叶的实例 jl_datatype_t 对象。 该 jl_typeof() 函数可以用来查询它:
jl_value_t *jl_typeof(jl_value_t *v);
对象的布局取决于其类型。 反射方法可用于检查该布局。 可以通过调用get-field方法之一来访问字段:
jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i);
jl_value_t *jl_get_field(jl_value_t *o, char *fld);
如果字段类型是已知的,先验地是所有指针,则值也可以直接作为数组访问提取:
jl_value_t *v = value->fieldptr[n];
作为一个例子,一个"盒装" uint16_t 存储如下:
struct {
opaque metadata;
struct {
uint16_t data; // -- 2 bytes
} jl_value_t;
};
此对象由 jl_box_uint16(). 请注意, jl_value_t 指针引用数据部分,而不是结构顶部的元数据。
在很多情况下,一个值可能被"拆箱"存储(只是数据,没有元数据,甚至可能不存储,只是保存在寄存器中),所以假设一个盒子的地址是一个唯一的标识符是不安全的。 "平均"测试(对应于 === 函数在Julia中),应改为用于比较两个未知对象以进行等价:
int jl_egal(jl_value_t *a, jl_value_t *b);
这种优化应该是相对透明的API,因为对象将被"盒装"按需,每当一个 jl_value_t 需要指针。
注意修改a jl_value_t 只有当对象是可变的时,才允许内存中的指针。 否则,修改值可能会损坏程序,结果将是未定义的。 值的可变性属性可以用:
int jl_is_mutable(jl_value_t *v);
如果被存储的对象是 jl_value_t,Julia垃圾收集器也必须通知:
void jl_gc_wb(jl_value_t *parent, jl_value_t *ptr);
然而, 嵌入朱莉娅部分,以涵盖各种类型的装箱和拆箱的其他细节,并理解gc的相互作用。
一些内置类型的镜像结构是https://github.com/JuliaLang/julia/blob/master/src/julia.h[定义于 朱莉娅。h]. 对应的全局 jl_datatype_t 对象由https://github.com/JuliaLang/julia/blob/master/src/jltypes.c[脧锚脧赂`jl_init_types` 在 jltypes。c].
垃圾收集器标记位
垃圾收集器使用来自 jl_typetag_t 来跟踪系统中的每个对象。 有关此算法的更多详细信息,请参阅https://github.com/JuliaLang/julia/blob/master/src/gc-stock.c[垃圾回收器实现在 gc-股票。c].
对象分配
大多数新对象由 jl_new_structv():
jl_value_t *jl_new_struct(jl_datatype_t *type, ...);
jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, uint32_t na);
虽然, 轨道,轨道对象也可以直接从内存中构造:
jl_value_t *jl_new_bits(jl_value_t *bt, void *data)
并且某些对象具有特殊的构造函数,必须使用这些构造函数来代替上述函数:
类别:
jl_datatype_t *jl_apply_type(jl_datatype_t *tc, jl_tuple_t *params);
jl_datatype_t *jl_apply_array_type(jl_datatype_t *type, size_t dim);
虽然这些是最常用的选项,但也有更多的低级构造函数,您可以在https://github.com/JuliaLang/julia/blob/master/src/julia.h[脧锚脧赂`朱莉娅。h`]. 这些用于 jl_init_types() 创建引导Julia系统映像创建所需的初始类型。
元组:
jl_tuple_t *jl_tuple(size_t n, ...);
jl_tuple_t *jl_tuplev(size_t n, jl_value_t **v);
jl_tuple_t *jl_alloc_tuple(size_t n);
元组的表示在Julia对象表示生态系统中是高度独特的。 在某些情况下,一个 基地。元组()object可以是指向元组包含的对象的指针数组,等价于:
typedef struct {
size_t length;
jl_value_t *data[length];
} jl_tuple_t;
但是,在其他情况下,元组可能会转换为匿名 等位,等位类型和存储的未装箱,或者它可能根本不存储(如果它没有在泛型上下文中作为 jl_value_t*).
符号:
jl_sym_t *jl_symbol(const char *str);
函数和方法安装:
jl_function_t *jl_new_generic_function(jl_sym_t *name);
jl_method_instance_t *jl_new_method_instance(jl_value_t *ast, jl_tuple_t *sparams);
数组:
jl_array_t *jl_new_array(jl_value_t *atype, jl_tuple_t *dims);
jl_array_t *jl_alloc_array_1d(jl_value_t *atype, size_t nr);
jl_array_t *jl_alloc_array_nd(jl_value_t *atype, size_t *dims, size_t ndims);
请注意,其中许多具有用于各种特殊用途的替代分配功能。 这里的列表反映了更常见的用法,但更完整的列表可以通过阅读https://github.com/JuliaLang/julia/blob/master/src/julia.h[脧锚脧赂`朱莉娅。h` 头文件]。
在Julia内部,存储通常由 新结构() (或 新奥比() 对于特殊类型):
jl_value_t *newstruct(jl_value_t *type);
jl_value_t *newobj(jl_value_t *type, size_t nfields);
在最低级别,通过对垃圾收集器的调用来分配内存(在 gc-股票。c),然后用它的类型标记:
jl_value_t *jl_gc_allocobj(size_t nbytes);
void jl_set_typeof(jl_value_t *v, jl_datatype_t *type);
|
过时警告函数的文档和用法 |
请注意,所有对象都以4字节的倍数分配,并与平台指针大小对齐。 内存是从池中为较小的对象分配的,或者直接与 马洛克() 对于大型物体。
|
单例类型单例类型只有一个实例,没有数据字段。 单例实例的大小为0字节,并且仅由其元数据组成。 例如 |
请参阅[单例类型](/manual/types#man-singleton-types)和[虚无和缺失值](/manual/faq#虚无和缺失值)