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