在 SBCL 中获取对象位长
因为字符串编码问题,引发笔者对SBCL的内存分配进行一次简单的探究。
起因
近期大量语言发生了突飞猛进的发展,很多古代语言从单纯的支持ASCII演变成支持utf8。 但是utf8并非是一个有效字符存储方案,所以在Erlang中一个中文字符会被定义为16位,也 就是我们常说的utf16,从而达到高效但非节约的存储。那么自己常用的Common Lisp中,一 个中文字符又是会被定义为多少位呢?
解决方案
首先要实现下面这函数
1 2 3 4 5 6 |
(defun get-object-size/octets (object) (sb-sys:without-gcing (nth-value 2 (sb-vm::reconstitute-object (ash ;; 因为返回了一个fixnum对象的lispobj,因此需要进行这一步的处理 (logandc1 sb-vm:lowtag-mask (sb-kernel:get-lisp-obj-address object)) ;; 掩码掩掉最后nbit,得到lispobj真正地址,n取决于平台,x64是4 (- sb-vm:n-fixnum-tag-bits)))))) |
为什么会有这个函数呢,因为lisp在创建对象的时候和Erlang是一致的,都会将指针的空位 设置为标签掩码。
接着我们测试一个中文字符
1 2 |
(defvar *c* "中") (get-object-size/octets *c*) ;; 32bit |
但是查阅了SBCL的代码的时候,发现
1 2 |
(def!constant sb!xc:char-code-limit #!-sb-unicode 256 #!+sb-unicode #x110000 "the upper exclusive bound on values produced by CHAR-CODE") |
也就是说,SBCL会使用一个32bit的位长来表示一个utf16的字符,具体可以参阅Memory Layout 。
总结
从这个测试中可以发现,绝大部份古代语言真对宽字符会直接处理成utf16,而不是utf8。 当然也有一些特例,例如OCaml,一个字符依然是8bits,对宽字符会处理成utf8。