2014-09-01 90 views
1

我试图通过对它的一些调查来了解Neo4j对象缓存。对象缓存的第一印象来自此链接中的幻灯片: http://www.slideshare.net/thobe/an-overview-of-neo4j-internals了解Neo4j对象缓存

具体来说,缓存中的节点/关系对象应该与幻灯片9或15/42类似。为了验证这一点,我使用现有图形数据库内容编写了一个简单的服务器脚本我这样做的方法是使用sun.misc.Unsafe来查看节点/关系对象的起始虚拟地址。获得虚拟地址的程序是从以下链接: How can I get the memory location of a object in java?

public static long addressOf(Object o) throws Exception { 
    Object[] array = new Object[] { o }; 

    long baseOffset = unsafe.arrayBaseOffset(Object[].class); 
    int addressSize = unsafe.addressSize(); 
    long objectAddress; 
    switch (addressSize) { 
    case 4: 
     objectAddress = unsafe.getInt(array, baseOffset); 
     break; 
    case 8: 
     objectAddress = unsafe.getLong(array, baseOffset); 
     break; 
    default: 
     throw new Error("unsupported address size: " + addressSize); 
    } 
    return (objectAddress); 
} 

而在Neo4j的服务器脚本(我的main()类),我按id得到节点地址,并以下列方式打印出来的地址:

void checkAddr(){ 
    nodeAddr(0); 
    nodeAddr(1); 
    nodeAddr(2); 
} 

void nodeAddr(int n){ 
    Node oneNode = graphDb.getNodeById(n); 
    Node[] array1 = {oneNode}; 

    try { 
     long address = UnsafeUtil.addressOf(array1); 
     System.out.println("Addess: " + address); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

首先,我试着用软缓存提供程序,这是默认情况。地址获得打印出来用于节点对象0,1和2是:

Addess:4168500044个 Addess:4168502383个 Addess:4168502753

因此,使用第二地址 - 第一地址和第三地址 - 第二个地址,我可以确切知道节点正在占用多少空间。在这种情况下,第一个节点对象需要2339B,第二个需要370B。

然后,看到禁用对象缓存的影响,我确实有NoCacheProvider设置:

调用setConfig(GraphDatabaseSettings.cache_type,NoCacheProvider.NAME)

的地址获得打印出来的:

Addess:4168488391 Addess:4168490708个 Addess:4168491056

与第一种情况类似计算的偏移量是:第一个节点对象需要2317B,第二个需要348B。

这里来我的问题:

  1. 由于我使用的是同一张图,做只读查询,为什么同一个节点对象的大小变化?

  2. 当我禁用对象缓存时,为什么地址偏移量看起来像存在对象缓存一样?例如,在节点存储文件中,单个节点需要9个字节,这在我的实验中不是这种情况。如果我得到节点对象的方式有问题,我如何以正确的方式获得虚拟地址?有什么办法可以知道mmap节点文件在内存中的位置?

  3. 我怎么能确切地知道存储在节点对象中的内容。当我在此链接处查看Node.class时: https://github.com/neo4j/neo4j/blob/1.9.8/community/kernel/src/main/java/org/neo4j/graphdb/Node.java 看起来节点对象看起来应该与在演示文稿幻灯片中看起来一样。而只是一组节点对象使用的函数。进一步是一个节点对象在no-object-cache和with-object-cache场合中同时作为一个整体进入内存?

回答

2

Node对象不是什么的Neo4j存储在“对象缓存”,这样你就不会通过查看这些实例获得多少洞察的Neo4j的缓存。 Neo4j为您提供的Node的实现是一个名为NodeProxy的类的实例,并且它们尽可能小(两个字段:内部标识和对数据库的引用)。这些只是用于在数据库中的节点周围执行操作的节点句柄。存储在“对象高速缓存”中的对象是一个名为NodeImpl的类的实例(尽管它们的名称没有实现接口Node)。 NodeImpl对象的形状在该演示文稿的第15张幻灯片(幻灯片中的第9页)中概述。那么,它大致有这样的形状,自从我制作这些幻灯片以来,Neo4j已经发展了。

Neo4j的演变也改变了节点记录在磁盘上占用的字节数。 Neo4j 2.0和更高版本的节点记录比这些幻灯片所呈现的略大。如果您有兴趣查看这些记录的布局,您应该查看NodeRecord类,然后从NodeStore类开始,“向下”进入其依赖关系以查找内存映射。

除了查看错误的对象以查看Neo4j中不同缓存方法之间的差异,您的测量方法存在缺陷。比较对象的地址不会告诉你任何关于这些对象的大小。 JVM并不保证依次分配的两个对象(在时间上)将驻留在内存中,即使JVM确实使用了这种分配策略,Neo4j可能已经在两个对象的分配之间分配了多个对象正在比较。然后是垃圾收集器,它可能会在您获取一个对象的地址和获取下一个对象的地址之间移动对象。因此,在Java中查看对象的地址对于任何事情来说都是非常有用的。为了更好地测量Java中对象的大小,请查看Java Object Layout utility,或使用Java代理中的Instrumentation.getObjectSize(...) method

要回答你的问题是说:

  1. 节点对象的尺寸并没有发生变化,它们的地址不能保证在运行之间是相同的。按照我上面的描述,你不能依靠对象地址来计算对象的大小。

  2. 既然您正在查看NodeProxy对象,无论Neo4j使用什么缓存策略,它们看起来都是一样的。为了查看NodeImpl对象,您必须深入挖掘Neo4j的内部。由于看起来您使用的是Neo4j 1.9,因此您会将GraphDatabaseService实例强制转换为GraphDatabaseAPI(实现内部的接口),然后调用该对象的getNodeManager()方法。从NodeManager您可以拨打getNodeIfCached(node.getId())获取NodeImpl对象。请注意,这个API不会在Neo4j版本之间兼容,并且使用它是那些“保修无效如果密封破损”的情况之一...

  3. 请看NodeImpl的源代码。至于何时以及如何将数据带入缓存,Neo4j会试图对此进行懒惰,只加载您使用的数据。如果您正在获取节点的关系,那么这些关系将被加载到缓存中,并且如果您正在获取属性,那么这些属性将被加载到缓存中。如果你只有关系,属性将永远不会被加载,反之亦然。