blob: d3f74bba974ee369174f24610041d09026256281 [file] [log] [blame] [view]
---
title: Advanced Features
sidebar_position: 7
id: advanced_features
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.
---
This page covers advanced features including zero-copy serialization, deep copy, memory management, and logging.
## Zero-Copy Serialization
Fory supports zero-copy serialization for efficient handling of large binary data:
```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 {
// Note that fory instance should be reused instead of creation every time.
static Fory fory = Fory.builder()
.withLanguage(Language.JAVA)
.build();
public static void main(String[] args) {
List<Object> list = Arrays.asList("str", new byte[1000], new int[100], new double[100]);
Collection<BufferObject> bufferObjects = new ArrayList<>();
byte[] bytes = fory.serialize(list, e -> !bufferObjects.add(e));
List<MemoryBuffer> buffers = bufferObjects.stream()
.map(BufferObject::toBuffer).collect(Collectors.toList());
System.out.println(fory.deserialize(bytes, buffers));
}
}
```
## Object Deep Copy
Fory provides efficient deep copy functionality:
### With Reference Tracking
```java
Fory fory = Fory.builder().withRefCopy(true).build();
SomeClass a = xxx;
SomeClass copied = fory.copy(a);
```
### Without Reference Tracking (Better Performance)
When disabled, deep copy will ignore circular and shared references. Same reference of an object graph will be copied into different objects in one `Fory#copy`:
```java
Fory fory = Fory.builder().withRefCopy(false).build();
SomeClass a = xxx;
SomeClass copied = fory.copy(a);
```
## Memory Allocation Customization
Fory provides a `MemoryAllocator` interface that allows you to customize how memory buffers are allocated and grown during serialization operations. This can be useful for performance optimization, memory pooling, or debugging memory usage.
### MemoryAllocator Interface
The `MemoryAllocator` interface defines two key methods:
```java
public interface MemoryAllocator {
/**
* Allocates a new MemoryBuffer with the specified initial capacity.
*/
MemoryBuffer allocate(int initialCapacity);
/**
* Grows an existing buffer to accommodate the new capacity.
* The implementation must grow the buffer in-place by modifying
* the existing buffer instance.
*/
MemoryBuffer grow(MemoryBuffer buffer, int newCapacity);
}
```
### Using Custom Memory Allocators
You can set a global memory allocator that will be used by all `MemoryBuffer` instances:
```java
// Create a custom allocator
MemoryAllocator customAllocator = new MemoryAllocator() {
@Override
public MemoryBuffer allocate(int initialCapacity) {
// Add extra capacity for debugging or pooling
return MemoryBuffer.fromByteArray(new byte[initialCapacity + 100]);
}
@Override
public MemoryBuffer grow(MemoryBuffer buffer, int newCapacity) {
if (newCapacity <= buffer.size()) {
return buffer;
}
// Custom growth strategy - add 100% extra capacity
int newSize = (int) (newCapacity * 2);
byte[] data = new byte[newSize];
buffer.copyToUnsafe(0, data, Platform.BYTE_ARRAY_OFFSET, buffer.size());
buffer.initHeapBuffer(data, 0, data.length);
return buffer;
}
};
// Set the custom allocator globally
MemoryBuffer.setGlobalAllocator(customAllocator);
// All subsequent MemoryBuffer allocations will use your custom allocator
Fory fory = Fory.builder().withLanguage(Language.JAVA).build();
byte[] bytes = fory.serialize(someObject); // Uses custom allocator
```
### Default Memory Allocator Behavior
The default allocator uses the following growth strategy:
- For buffers smaller than `BUFFER_GROW_STEP_THRESHOLD` (100MB): multiply capacity by 2
- For larger buffers: multiply capacity by 1.5 (capped at `Integer.MAX_VALUE - 8`)
This provides a balance between avoiding frequent reallocations and preventing excessive memory usage.
### Use Cases
Custom memory allocators are useful for:
- **Memory Pooling**: Reuse allocated buffers to reduce GC pressure
- **Performance Tuning**: Use different growth strategies based on your workload
- **Debugging**: Add logging or tracking to monitor memory usage
- **Off-heap Memory**: Integrate with off-heap memory management systems
## Logging
### ForyLogger
By default, Fory uses a custom logger `ForyLogger` for internal needs. It builds resulting logged data into a single string and sends it directly to `System.out`. The result line layout is similar to (in Log4j notation):
```
%d{yyyy-MM-dd hh:mm:ss} %p %C:%L [%t] - %m%n
```
The layout can't be changed.
Example output:
```
2025-11-07 08:49:59 INFO CompileUnit:55 [main] - Generate code for org.apache.fory.builder.SerializedLambdaForyCodec_0 took 35 ms.
2025-11-07 08:50:00 INFO JaninoUtils:121 [main] - Compile [SerializedLambdaForyCodec_0] take 144 ms
```
### Slf4jLogger
If a more sophisticated logger is required, configure Fory to use Slf4j via `LoggerFactory.useSlf4jLogging()`. For example, enabling Slf4j before creating Fory:
```java
public static final ThreadSafeFory FORY;
static {
LoggerFactory.useSlf4jLogging(true);
FORY = Fory.builder()
.buildThreadSafeFory();
}
```
**Note:** Enabling Slf4j via `useSlf4jLogging` will be ignored when the application runs in a GraalVM native image.
### Suppress Fory Logs
Both `ForyLogger` and `Slf4jLogger` allow controlling log output level or suppressing logs entirely. Configure logger level via `LoggerFactory.setLogLevel()`:
```java
static {
// to log only WARN and higher
LoggerFactory.setLogLevel(LogLevel.WARN_LEVEL);
// to disable logging entirely
LoggerFactory.disableLogging();
}
```
**Note:** Selected logging level is applied before Slf4j implementation's logger level. So if you set `WARN_LEVEL` (as in the example above) then you will not see INFO messages from Fory even if INFO is enabled in Logback.
## Related Topics
- [Compression](compression.md) - Data compression options
- [Configuration Options](configuration.md) - All ForyBuilder options
- [Cross-Language Serialization](cross-language.md) - XLANG mode