title: Zero-Copy Serialization sidebar_position: 50 id: zero_copy license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Zero-copy serialization allows large binary data (byte arrays, numeric arrays) to be serialized out-of-band, avoiding memory copies and reducing serialization overhead.

When to Use Zero-Copy

Use zero-copy serialization when:

  • Serializing large byte arrays or binary blobs
  • Working with numeric arrays (int[], double[], etc.)
  • Transferring data over high-performance networks
  • Memory efficiency is critical

How It Works

  1. Serialization: Large buffers are extracted and returned separately via a callback
  2. Transport: The main serialized data and buffer objects are transmitted separately
  3. Deserialization: Buffers are provided back to reconstruct the original object

This avoids copying large data into the main serialization buffer.

Java

import org.apache.fory.*;
import org.apache.fory.config.*;
import org.apache.fory.serializer.BufferObject;
import org.apache.fory.memory.MemoryBuffer;

import java.util.*;
import java.util.stream.Collectors;

public class ZeroCopyExample {
  public static void main(String[] args) {
    Fory fory = Fory.builder().withXlang(true).build();

    // Data with large arrays
    List<Object> list = List.of(
        "str",
        new byte[1000],    // Large byte array
        new int[100],      // Large int array
        new double[100]    // Large double array
    );

    // Collect buffer objects during serialization
    Collection<BufferObject> bufferObjects = new ArrayList<>();
    byte[] bytes = fory.serialize(list, e -> !bufferObjects.add(e));

    // Convert to buffers for transport
    List<MemoryBuffer> buffers = bufferObjects.stream()
        .map(BufferObject::toBuffer)
        .collect(Collectors.toList());

    // Deserialize with buffers
    Object result = fory.deserialize(bytes, buffers);
    System.out.println(result);
  }
}

Python

import array
import pyfory
import numpy as np

fory = pyfory.Fory(xlang=True)

# Data with large arrays
data = [
    "str",
    bytes(bytearray(1000)),           # Large byte array
    array.array("i", range(100)),     # Large int array
    np.full(100, 0.0, dtype=np.double) # Large numpy array
]

# Collect buffer objects during serialization
serialized_objects = []
serialized_data = fory.serialize(data, buffer_callback=serialized_objects.append)

# Convert to buffers for transport
buffers = [obj.to_buffer() for obj in serialized_objects]

# Deserialize with buffers
result = fory.deserialize(serialized_data, buffers=buffers)
print(result)

Go

package main

import forygo "github.com/apache/fory/go/fory"
import "fmt"

func main() {
  serializer := forygo.NewFory(forygo.WithXlang(true))

  // Data with large arrays
  list := []any{
    "str",
    make([]byte, 1000), // Large byte array
  }

  buf := forygo.NewByteBuffer(nil)
  var bufferObjects []forygo.BufferObject

  // Collect buffer objects during serialization
  if err := serializer.SerializeWithCallback(buf, list, func(o forygo.BufferObject) bool {
    bufferObjects = append(bufferObjects, o)
    return false
  }); err != nil {
    panic(err)
  }

  // Convert to buffers for transport
  var buffers []*forygo.ByteBuffer
  for _, o := range bufferObjects {
    buffers = append(buffers, o.ToBuffer())
  }

  // Deserialize with buffers
  var newList []any
  if err := serializer.DeserializeWithCallbackBuffers(buf, &newList, buffers); err != nil {
    panic(err)
  }
  fmt.Println(newList)
}

JavaScript

// Zero-copy support coming soon

Use Cases

High-Performance Data Transfer

When sending large datasets over the network:

// Sender
Collection<BufferObject> buffers = new ArrayList<>();
byte[] metadata = fory.serialize(dataObject, e -> !buffers.add(e));

// Send metadata and buffers separately
network.sendMetadata(metadata);
for (BufferObject buf : buffers) {
    network.sendBuffer(buf.toBuffer());
}

// Receiver
byte[] metadata = network.receiveMetadata();
List<MemoryBuffer> buffers = network.receiveBuffers();
Object data = fory.deserialize(metadata, buffers);

Memory-Mapped Files

Zero-copy works well with memory-mapped files:

// Write
Collection<BufferObject> buffers = new ArrayList<>();
byte[] data = fory.serialize(largeObject, e -> !buffers.add(e));
writeToFile("data.bin", data);
for (int i = 0; i < buffers.size(); i++) {
    writeToFile("buffer" + i + ".bin", buffers.get(i).toBuffer());
}

// Read
byte[] data = readFromFile("data.bin");
List<MemoryBuffer> buffers = readBufferFiles();
Object result = fory.deserialize(data, buffers);

Performance Considerations

  1. Threshold: Small arrays may not benefit from zero-copy due to callback overhead
  2. Network: Zero-copy is most beneficial when buffers can be sent without copying
  3. Memory: Reduces peak memory usage by avoiding buffer copies

See Also