在不同虚拟机之间高效复制对象
我感觉我可能要问一个“傻问题”,但我还是得问……
我有两个虚拟机。
我想把一个对象的实例从一个虚拟机复制到另一个虚拟机。
这样做有可能吗?我想把这个对象在虚拟机内存中的数据直接复制过去,另一台虚拟机只需要在它的内存中分配这些数据,并在它的栈中添加一个指向这个内存位置的引用就行了……
目前,为了实现这个功能,我们需要先把对象序列化,然后再反序列化,这样做效率低得多(从计算的角度来看),比起直接复制实例来说,解析过程简直是浪费计算资源……
举个例子:在JavaScript中,每个虚拟机都是一个V8
的实例,一种做法是把对象转换成JSON格式(JSON.stringify
),然后以某种方式把这个字符串发送到另一台虚拟机,另一台虚拟机接收到字符串后再把它转换回对象(比如说:var myObject = eval('(' + myJSONtext + ')');
)……(这里用JavaScript只是个例子,这算是一种序列化)
3 个回答
我可以肯定,在VMware的API中没有办法直接进行这种内存传输;我不太清楚其他虚拟机管理程序,但我还是有点怀疑。VMware有一些方法可以把整个机器的内存转移到另一台主机上(主要是通过使用分页文件),但是没有办法只提取一个正在运行的程序中的一部分内存并把它给另一个程序——这涉及的东西太多了。
所以你现在使用的对象序列化方法绝对是一个不错且常见的解决方案,幸运的是,你正在使用的编程语言都有很好的选择(Python,Java)。
不过我在想,你真的需要把整个对象都存起来再重建吗,还是说只需要其中的一部分数据。如果数据量不大,你可以使用某种远程方法调用,从源虚拟机发送一条消息给接收方,告诉它用这些数据创建一个对象。在这种情况下,你只需序列化必要的数据,让目标机器在自己的内存中重建这个对象。
为什么不使用cpickle呢?它可以非常可靠且快速地将数据转换成一种可以传输的格式,然后你可以通过网络连接、命名管道、内存映射等方式发送这些数据。只要在传输过程中没有损坏,并且使用的pickle模块版本差别不大,你就可以在另一端可靠地将这些数据重新组装起来。当然,真正更专业的做法是使用一种不依赖于平台的标准,比如XML,这样可以让你在不同的平台之间更好地互通。我知道这有点偏离了问题,但我觉得那些参与过Python解释器代码的人可以为你进一步解释。
先不考虑一个简单的想法,那就是你可以很容易地在多个虚拟机(VM)之间推广这个问题。任何尝试建立这样的机制都将严重依赖于你为其构建机制的虚拟机的具体实现细节。
以下是几个为什么这件事不常见的原因:
内存中的表示方式通常在不同的架构之间不兼容。如果我想把一个“对象”从一个SPARC机器上的虚拟机发送到一个x86机器上的虚拟机,而我对它的结构一无所知,那么在另一边这个对象可能会变得损坏。
对象在两台机器上的内存位置不一定相同,所以对象内部的指针在到达第二台虚拟机后需要进行修补。这同样需要对对象的结构有内部了解。
对象可能包含对其他对象的引用,因此复制一个对象意味着要复制一整棵对象树,而且通常不是一个无环的树。为了可靠地做到这一点,你最终会写出看起来像序列化库的代码。
对象通常会持有一些本地资源(比如文件句柄和套接字),这些资源无法可靠地在机器之间传输。
在许多虚拟机中,数据(你想复制的对象)和元数据(例如,你想复制的对象的类)是有区别的。在这些虚拟机中,即使你能够逐位复制对象而不受损坏,它可能还依赖于一些在远程端不存在的元数据。逐位复制元数据也很棘手,因为许多虚拟机使用一些实现技术(比如全局字符串池或内存映射的对象代码),使得数据本质上不可移植。你可能还会得到比你想要的更多的元数据(例如,在.net中,最小的元数据单元通常是一个程序集)。
内存中的表示方式通常在同一虚拟机的不同版本之间不兼容,并且不包含可以用来修补数据的内部版本信息。
内存中的表示包含很多不需要复制的东西(例如,内联缓存、垃圾回收信息)。复制这些东西是浪费,而且这些信息在另一边可能没有意义。
总的来说,要可靠地做到这一点,你最终会建立一个世界上最笨拙和不可靠的序列化库,而简单的内存复制所带来的性能提升在修补许多在简单复制时损坏的东西时就会消失。
因此,这些机制往往不存在。
不过,这条规则有一个巨大的例外:基于图像的虚拟机(比如许多Smalltalk和Self虚拟机)是围绕着虚拟机状态存在于可以复制、在机器之间移动的“图像”这个想法构建的。这通常会带来相当大的性能损失。