Java 理解Neo4j对象缓存

Java 理解Neo4j对象缓存,java,object,neo4j,heap,graph-databases,Java,Object,Neo4j,Heap,Graph Databases,我试图通过对Neo4j对象缓存的一些调查来了解它。我对对象缓存的第一印象来自此链接中的幻灯片: 具体来说,缓存中的节点/关系对象应该类似于幻灯片9或15/42。为了验证这一点,我使用现有的图形数据库内容编写了一个简单的服务器脚本。我这样做的方式是尝试使用sun.misc.Unsafe查看节点/关系对象的起始虚拟地址。获取虚拟地址的程序来自以下链接: 在neo4j服务器脚本(My main()类)中,我通过id获取节点地址,并按以下方式打印出地址: void checkAddr(){

我试图通过对Neo4j对象缓存的一些调查来了解它。我对对象缓存的第一印象来自此链接中的幻灯片:

具体来说,缓存中的节点/关系对象应该类似于幻灯片9或15/42。为了验证这一点,我使用现有的图形数据库内容编写了一个简单的服务器脚本。我这样做的方式是尝试使用sun.misc.Unsafe查看节点/关系对象的起始虚拟地址。获取虚拟地址的程序来自以下链接:

在neo4j服务器脚本(My 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的地址为:

增:4168500044 增:4168502383 增:4168502753

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

然后,为了查看禁用对象缓存的影响,我使用NoCacheProvider执行以下设置:

setConfig(GraphDatabaseSettings.cache\u类型,NoCacheProvider.NAME)

打印出来的地址是:

增:4168488391 增:4168490708 增:4168491056

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

我的问题来了:

  • 既然我使用相同的图形并执行只读查询,为什么相同节点对象的大小会发生变化

  • 当我禁用对象缓存时,为什么地址偏移看起来与存在对象缓存时相同?例如,在节点存储文件中,单个节点需要9个字节,这在我的实验中不是这样的。如果获取节点对象的方式有问题,如何以正确的方式获取虚拟地址?我有没有办法知道mmap节点文件在内存中的具体位置

  • 我如何确切地知道节点对象中存储了什么。当我在这个链接上查看Node.class时: 节点对象的外观似乎与演示幻灯片中的不同。而只是节点对象使用的一组函数。此外,在无对象缓存和有对象缓存的情况下,节点对象是否同时作为一个整体带入内存


  • 节点
    对象不是Neo4j存储在“对象缓存”中的对象,因此,通过查看这些实例,您不会对Neo4j的缓存有太多了解。Neo4j提供给您的
    节点的实现是名为的类的实例,并且尽可能小(两个字段:内部id和对数据库的引用)。它们只是作为节点的句柄,用于在数据库中围绕该节点执行操作。存储在“对象缓存”中的对象是名为的类的实例(尽管名称不同,但它们不实现
    节点
    接口)。
    NodeImpl
    对象的形状如该演示文稿中第15张幻灯片(第9页)所示。嗯,它大致就是这个形状,Neo4j是从我制作这些幻灯片开始进化的

    Neo4j evolving还改变了节点记录在磁盘上占用的字节数。Neo4j 2.0及更高版本的节点记录比幻灯片显示的稍大。如果您对查看这些记录的布局感兴趣,那么应该查看类,然后从类开始“向下”查看其依赖项,以找到内存映射

    除了在Neo4j中查看不同缓存方法之间的差异时看错了对象之外,您的测量方法也是有缺陷的。比较对象的地址并不能告诉您这些对象的大小。JVM不保证一个接一个(在时间上)分配的两个对象将相邻地驻留在内存中,即使JVM使用了这样的分配策略,Neo4j也可能在您正在比较的两个对象的分配之间分配了多个对象。然后是垃圾收集器,它可能在获取一个对象的地址和获取下一个对象的地址之间移动了对象。因此,在Java中查看对象的地址几乎没有任何用处。要获得在Java中测量对象大小的更好方法,请查看,或使用来自Java代理的

    要回答您提出的问题:

  • 节点对象的大小不变,它们的地址不能保证在两次运行之间相同。根据我上面的描述,您不能依赖对象地址来计算对象大小

  • 由于您查看的是
    NodeProxy
    对象,因此无论Neo4j使用何种缓存策略,它们看起来都是一样的。为了查看
    NodeImpl
    对象,您必须深入研究Neo4j的内部结构。因为看起来您正在使用Neo4j 1.9,所以您需要将
    GraphDatabaseService
    实例强制转换为
    GraphDatabaseAPI
    (实现内部的接口),然后在该对象上调用
    getNodeManager()
    方法。从
    NodeManager
    可以调用
    getNodeIfCached(node.getId())
    来获取
    NodeImpl
    对象。请注意,此API在Neo4j版本之间不兼容,使用此API属于“密封破损则保修无效”的情况之一

  • 请查看
    NodeImpl
    的源代码。至于何时以及如何将数据带入缓存,Neo4j试图在这方面表现得很懒散,只加载您使用的数据。如果要获取节点的关系,这些关系将加载到缓存中,如果要获取属性,
    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();
        }
    }