Search code examples
javaserializationgraphstack-overflow

How to keep Reference to a Serialized object in Java?


How can i keep reference to an object that i'm sure it'll be serialized but i don't want it to be serialized through this reference ?

To make it more clear i've a Network that contains list of Nodes each Node contains a list of Connections, the Connection contains a reference to another Nodes.

The problem is when i try serializing kind of big Network this results to StackOverflowError, the situation that i'm assuming that causes this is the following:

  • Serialization starts with the Network and tries to serialize the first Node
  • Then it tries to serialize the first connection which contains a reference to another node
  • It tries to serialize the next Node and so on, and the serialization recursion goes through all nodes causing the overflow
  • If the references in Connection are marked as transient there is no problem in serialization, but then the references = null after deserialization

Here is a sample that reproduces the problem

Network.java

import java.io.Serializable;
import java.util.ArrayList;

public class Network implements Serializable {
    private static final long serialVersionUID = 1399116563470735156L;

    ArrayList<Layer>layers;

    public Network() {
        layers= new ArrayList<Layer>();
    }

    //connect each layer to next layer
    public void connectAllLayers(){
        for (int i = 0; i < layers.size()-1; i++) {
            layers.get(i).connectTo(layers.get(i+1));
        }
    }
}

Layer.java

import java.io.Serializable;
import java.util.ArrayList;


public class Layer implements Serializable{
    private static final long serialVersionUID = -5519150448729707106L;

    public ArrayList<Node>nodes;

    public Layer(int nodeCount) {
        nodes = new ArrayList<Node>();
        for (int i = 0; i < nodeCount; i++) {
            nodes.add(new Node());
        }
    }

    //connect all nodes in a layer to all nodes in the other layer
    public void connectTo(Layer layer){
        for (Node myNode : nodes) {
            for (Node toNode : nodes) {
                myNode.connectTo(toNode);
            }
        }
    }
}

Node.java

import java.io.Serializable;
import java.util.ArrayList;


public class Node implements Serializable{
    private static final long serialVersionUID = 6323513316304801012L;

    public ArrayList<Connection>connections;
    public double x,y,z,a,b,c;//some variables to simulate memory usage

    public Node() {
        connections = new ArrayList<Connection>();
        x=15;
    }

    public void connectTo(Node node){
        Connection connection = new Connection(this, node);
        this.connections.add(connection);
        node.connections.add(connection);
    }
}

Connection.java

import java.io.Serializable;

public class Connection implements Serializable {
    private static final long serialVersionUID = 7578299749680304407L;

    public Node from;
    public Node to;
    public double weight;

    public Connection(Node from, Node to) {
        this.from = from;
        this.to = to;
    }
}

Main.java

import java.io.*;

public class Main {

    public static void saveNetwork(Network net, String filename) {
        try {
            // Serialize data object to a file
            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream(filename));
            out.writeObject(net);
            out.close();
        } catch (IOException e) {
        }
    }

    public static Network loadNetwork(String filename) {
        Network net = null;
        try {
            FileInputStream door = new FileInputStream(filename);
            ObjectInputStream reader = new ObjectInputStream(door);
            net = (Network) reader.readObject();
            reader.close();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        return net;
    }

    public static void main(String[] args) {
        Network net;
        boolean load = false;//Change to true to load the saved object

        if (!load) {
            net = new Network();
            net.layers.add(new Layer(400));
            net.layers.add(new Layer(300));
            net.layers.add(new Layer(10));

            net.connectAllLayers();

            saveNetwork(net, "mynet");
        } else {
            net = loadNetwork("mynet");
        }
        Layer layer = net.layers.get(0);
        Node node = layer.nodes.get(0);
        Connection connection = node.connections.get(0);
        System.out.println(connection.to.x);
    }

}

To make the question more general, is there someway to serialize Graph like connected Node classes assuming there is a list that contains references to all Nodes, without causing overflow ?


Solution

  • Java will serialize graphs fine, storing only references to objects that were already serialized. However, it does use recursion in a depth-first fashion, and your graph is very deep (548 nodes deep before I got a stack overflow).

    Sort your layers by depth, and serialize them in descending order of depth. This will prevent deep recursion during serialization.