Project1

标题: Ruby 1.8(VX) &1.9(ACE) 的 String 的 object_id 与内存 [打印本页]

作者: 一箭烂YiJL    时间: 2012-1-15 11:43
标题: Ruby 1.8(VX) &1.9(ACE) 的 String 的 object_id 与内存
首先,
为了取得某个 String 的指针,我在 VX/RGE/XP 下写这个脚本:
  1. RtlMoveMemory_pi = Win32API.new("kernel32", "RtlMoveMemory", "pii", "i")
  2. ab = "abcdefg"
  3. address = "xxxx"
  4. RtlMoveMemory_pi.call(address, ab.object_id * 2 + 12, address.size)
  5. address = address.unpack("L")[0]   # 这个就是内存位置的开头
  6. buff = " " * ab.size               # 将成为 "abcdefg" 的复制本
  7. RtlMoveMemory_pi.call(buff, address, ab.size)
  8. p buff
复制代码
那时候的 object_id * 2 + 12 是"撞"出来的,所以我没有查到 Ruby 的内存结构。
ACE 的时候,发现不管用,而且出现全"\0"和全"?",并且程序有一定几率死掉(似乎因为读取内存不属于程序)。

问题是结构和 object_id 有什么分别?
作者: 剑兰的马甲    时间: 2012-1-15 18:04
顶起来,果然没有人讨论这个么T.T....
作者: 灼眼的夏娜    时间: 2012-1-15 18:17
class String
  
  Lstrcpyn = Win32API.new("kernel32", "lstrcpyn", "ppi", "l")
  
  def addr
    return Lstrcpyn.call(self, 0.chr, 0)
  end

end
用这个取字符串地址试试(objectid 没看……额
作者: 一箭烂YiJL    时间: 2012-1-15 18:36
灼眼的夏娜 发表于 2012-1-15 18:17
class String
  
  Lstrcpyn = Win32API.new("kernel32", "lstrcpyn", "ppi", "l")

呵。完全忘记了 Win32API 有 C/C++ 的字符串函数。
嗯...什巧妙的,但似乎违背了这个函数的意义- -||。结果(结论):
VX(Ruby 1.8):addr = RtlMoveMemory(object_id * 2 + 12, 4)
ACE(Ruby 1.9):addr = object_id * 2 + 8
作者: 第七水螰    时间: 2012-1-16 00:57
這個可以查 1.9 的 ruby.h,裏面有 RString 的定義(<-- 在頭文件裏進行定義的習慣不好)。
  1. struct RString {
  2.     struct RBasic basic;
  3.     union {
  4.         struct {
  5.             long len;
  6.             char *ptr;
  7.             union {
  8.                 long capa;
  9.                 VALUE shared;
  10.             } aux;
  11.         } heap;
  12.         char ary[RSTRING_EMBED_LEN_MAX + 1];
  13.     } as;
  14. };
复制代码
下面還有一個返回指針的宏:
  1. #define RSTRING_PTR(str) \
  2.     (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \
  3.      RSTRING(str)->as.ary : \
  4.      RSTRING(str)->as.heap.ptr)
复制代码
[/code]
在 string.c 裏可以看到 Ruby 1.9 是如何分配新字符串的,裏面有這麼一段:
  1.     if (len > RSTRING_EMBED_LEN_MAX) {
  2.         RSTRING(str)->as.heap.aux.capa = len;
  3.         RSTRING(str)->as.heap.ptr = ALLOC_N(char,len+1);
  4.         STR_SET_NOEMBED(str);
  5.     }
复制代码
可見 Ruby 1.9 對較短的字符串作了優化,當字符串長度小於等於 RSTRING_EMBED_LEN_MAX 的時候就直接把字符串嵌入到 RString 這個對象結構裏,而不是重新在堆裏分配內存,避免了頻繁 malloc 小內存塊帶來的內存碎片開銷。當把字符串嵌入 RString 裏的時候,那些堆的信息就不需要了;當在堆中分配字符串的時候,用於嵌入字符串的地址 RString#as.ary 就不需要了,所以才有外部的那個 union。

  1. #define RSTRING_EMBED_LEN_MAX ((int)((sizeof(VALUE)*3)/sizeof(char)-1))
复制代码
這個定義在 Win32 環境下應該總是展開為 11。你不妨試試長一點的字串。
作者: 亿万星辰    时间: 2012-1-21 22:53
在ACE里 ["a","b","c"].to_s 会得到一个很囧的结果……
作者: 第七水螰    时间: 2012-1-21 23:20
亿万星辰 发表于 2012-1-21 22:53
在ACE里 ["a","b","c"].to_s 会得到一个很囧的结果……

1.9 行為改變了:
enum.to_s → str
Returns a string representation of enum. (Prior to Ruby 1.9, this representation was the same as enum.join. Now it is the array as a literal.)
[ 1, 3, 5, 7, 9 ].to_s # => "[1, 3, 5, 7, 9]





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1