跳到主要内容
版本:0.17

引用跟踪

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

概述​

Reference tracking enables:

  • Shared references: Same object referenced multiple times is serialized once
  • Circular references: Objects that reference themselves or form cycles
  • Memory efficiency: No duplicate data for repeated objects

启用引用跟踪​

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();

线格式​

When reference tracking is enabled, nullable fields write a ref flag byte before the value:

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

Where ref_flag is:

ValueMeaning
-1 (NULL_FLAG)Value is null
-2 (NOT_NULL_VALUE_FLAG)Value is present, first occurrence
≥0Reference ID pointing to previously serialized object

引用跟踪与可空性​

These are independent concepts:

ConceptPurposeControlled By
NullabilityWhether a field can hold null valuesField type (Optional<T>) or annotation
Reference TrackingWhether duplicate objects are deduplicatedGlobal refTracking option

Key behavior:

  • Ref flag bytes are only written for nullable fields
  • Non-nullable fields skip ref flags entirely, even with refTracking=true
  • Reference deduplication only applies to objects that appear multiple times
// Reference tracking enabled, but non-nullable fields still skip ref flags
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
.build();

Per-Field Reference Tracking​

By default, most fields do not track references even when global refTracking=true. Only specific pointer/smart pointer types track references by default.

默认行为 by Language​

LanguageDefault Ref TrackingTypes That Track Refs by Default
JavaNoNone (use annotation to enable)
PythonNoNone (use annotation to enable)
GoNoNone (use fory:"ref" to enable)
C++Nostd::shared_ptr<T>
RustNoRc<T>, Arc<T>, Weak<T>

Customizing Per-Field Ref Tracking​

Java: @ForyField Annotation​

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 Wrapper​

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: Field Attributes​

#[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: Struct Tags​

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"`
}

When to Enable Per-Field Ref Tracking​

Enable ref tracking for fields that:

  • May contain the same object instance multiple times
  • Are part of circular reference chains
  • Hold large objects that might be shared

Disable (or leave default) for fields that:

  • Always contain unique values
  • Are primitives or simple value types
  • Don't participate in object sharing

Example: Shared References​

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)

Example: Circular References​

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

Language Support​

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​