跳到主要内容
版本:1.0.0

引用跟踪

本页说明 Fory 在跨语言序列化中如何处理共享引用与循环引用的引用跟踪。

概述

引用跟踪支持:

  • 共享引用:同一对象被多次引用时只序列化一次
  • 循环引用:对象引用自身或形成环
  • 内存效率:重复对象不会产生重复数据

启用引用跟踪

Java

Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
.build();

Python

fory = pyfory.Fory(xlang=True, ref_tracking=True)

Go

fory := forygo.NewFory(true) // true enables ref tracking

C++

auto fory = fory::Fory::create(fory::Config{
.ref_tracking = true
});

Rust

let fory = Fory::builder()
.with_ref_tracking(true)
.build();

编码格式

启用引用跟踪后,可空字段会在值之前写入 ref 标记字节

[ref_flag] [value data if not null/ref]

其中 ref_flag 为:

含义
-1 (NULL_FLAG)值为 null
-2 (NOT_NULL_VALUE_FLAG)值存在,且是首次出现
≥0指向此前已序列化对象的引用 ID

引用跟踪与可空性

二者是相互独立的概念:

概念目的控制方式
可空性字段是否可以保存 null 值字段类型(Optional<T>)或注解
引用跟踪是否对重复对象去重全局 refTracking 选项

关键行为:

  • ref 标记字节只会为可空字段写入
  • 即使 refTracking=true,不可空字段也完全跳过 ref 标记
  • 引用去重只适用于多次出现的对象
// Reference tracking enabled, but non-nullable fields still skip ref flags
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
.build();

按字段配置引用跟踪

默认情况下,即使全局 refTracking=true大多数字段也不会跟踪引用。只有特定指针/智能指针类型默认跟踪引用。

各语言默认行为

语言默认引用跟踪默认跟踪引用的类型
Java无(使用注解启用)
Python无(使用注解启用)
Go无(使用 fory:"ref" 启用)
C++std::shared_ptr<T>
RustRc<T>Arc<T>Weak<T>

自定义字段级引用跟踪

Java:@ForyField 注解

public class Document {
// Default: no ref tracking
String title;

// Enable ref tracking for this field
@ForyField(trackingRef = true)
Author author;

// Shared across documents, track refs to avoid duplicates
@ForyField(trackingRef = true)
List<Tag> tags;
}

C++:fory::field 包装器

struct Document {
std::string title;

// shared_ptr tracks refs by default
std::shared_ptr<Author> author;

// Explicitly enable ref tracking
fory::field<std::vector<Tag>, 1, fory::track_ref<true>> tags;

// Explicitly disable ref tracking
fory::field<std::shared_ptr<Data>, 2, fory::track_ref<false>> data;
};
FORY_STRUCT(Document, title, author, tags, data);

Rust:字段属性

#[derive(Fory)]
#[tag("example.Document")]
struct Document {
title: String,

// Rc/Arc track refs by default
author: Rc<Author>,

// Explicitly enable ref tracking
#[track_ref]
tags: Vec<Tag>,
}

Go:结构体 tag

type Document struct {
Title string

// Enable ref tracking for pointer to struct
Author *Author `fory:"ref"`

// Enable ref tracking for slice
Tags []Tag `fory:"ref"`
}

何时启用字段级引用跟踪

以下字段应启用引用跟踪:

  • 可能多次包含同一个对象实例
  • 参与循环引用链
  • 持有可能被共享的大对象

以下字段应禁用或保持默认:

  • 始终包含唯一值
  • 是基本类型或简单值类型
  • 不参与对象共享

示例:共享引用

public class Container {
List<String> data;
List<String> sameData; // Points to same list
}

Container obj = new Container();
obj.data = Arrays.asList("a", "b", "c");
obj.sameData = obj.data; // Shared reference

// With refTracking=true: data serialized once, sameData stores reference ID
// With refTracking=false: data serialized twice (duplicate)

示例:循环引用

public class Node {
String value;
Node next;
}

Node a = new Node("A");
Node b = new Node("B");
a.next = b;
b.next = a; // Circular reference

// With refTracking=true: works correctly
// With refTracking=false: infinite recursion error

语言支持

LanguageShared RefsCircular Refs
JavaYesYes
PythonYesYes
GoYesYes
C++YesYes
JavaScriptYesYes
RustYesNo (ownership rules)

Performance Considerations

  • Overhead: Reference tracking adds a hash map lookup per object
  • When to enable: Use when data has shared/circular references
  • When to disable: Use for simple data structures without sharing

See Also