序列化Java对象而不使用StackOverflower错误

序列化Java对象而不使用StackOverflower错误,java,serialization,stack-overflow,Java,Serialization,Stack Overflow,我在内存中有一个相当大的Java对象,它表示一个带有顶点和边的图。每个顶点都有一个与其连接的其他顶点的ArrayList(并且有一个HashMap数据结构,用于其他目的)。图形可以有几千个顶点和更多的边 当尝试使用Java的内置序列化来序列化图形时(实现可序列化,等等),我总是遇到一个堆栈溢出错误。将图形的其他属性设置为瞬态,也没有帮助,将堆栈大小设置为更大(即-Xss1g或-Xss512m) 我不认为我需要定制writeObject方法,因为ArrayList和HashMap已经有了自己的实现

我在内存中有一个相当大的Java对象,它表示一个带有顶点和边的图。每个顶点都有一个与其连接的其他顶点的
ArrayList
(并且有一个
HashMap
数据结构,用于其他目的)。图形可以有几千个顶点和更多的边

当尝试使用Java的内置序列化来序列化图形时(
实现可序列化
,等等),我总是遇到一个
堆栈溢出错误
。将图形的其他属性设置为
瞬态
,也没有帮助,将堆栈大小设置为更大(即
-Xss1g
-Xss512m

我不认为我需要定制
writeObject
方法,因为
ArrayList
HashMap
已经有了自己的实现,它们在序列化时被调用

我的问题是:有没有一种方法可以序列化内存中已经存在的大型Java对象,而不必获取
StackOverflowerError

编辑:以下是堆栈跟踪:

Exception in thread "main" java.lang.StackOverflowError
at java.lang.reflect.Method.invoke(Method.java:575)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:950)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1482)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at java.util.ArrayList.writeObject(ArrayList.java:570)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
// Many more lines after this
以下是我的
图形
课程概述:

public class Graph implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = -2632163054149021990L;
private ArrayList<Vertex> vertices;

private HashMap<Integer, Set<Vertex>> map;

public Graph(int rowMax, int colMax)
{
    map = new HashMap<Integer, Set<Vertex>>();

    this.vertices = new ArrayList<Vertex>();
}

public void connectVertices(Vertex u, Vertex v)
{
    u.addNeighbor(v);
    v.addNeighbor(u);
}

// other unrelated methods after this
public class Vertex implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = 8520500010710631610L;
public int row;
public int col;
private ArrayList<Vertex> neighbors; // may change this to Set<Vertex>

public Vertex(int i, int j)
{
    this.row = i;
    this.col = j;
    this.neighbors = new ArrayList<Vertex>();
}

public boolean addNeighbor(Vertex v)
{
    this.neighbors.add(v);
    return true;
}

// unrelated methods after this

编辑2:同样,对于尺寸较小但有“邻居”的图形,没有此问题。

如果图形深度太大,默认序列化无法处理,序列化将抛出
堆栈溢出错误。这是由于默认序列化在解析图形时递归序列化每个节点

平面结构可以正常工作(例如,具有2000个子节点的父节点),但深层结构将失败(例如,具有2000个子级别的节点)

例如,以下情况将导致堆栈溢出:

public class Node implements Serializable
{
    private ArrayList<Node> nodes = new ArrayList<Node>();

    public static void main(String[] args) throws Exception
    {
        Node node = new Node();
        int depth = 3000;

        // Add nodes chained down to specified depth
        Node last = node;
        for (int i = 0; i < depth; i++)
        {
            Node temp = new Node();
            last.nodes.add(temp);
            last = temp;
        }

        System.out.println("starting");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        // Below line will cause a stack overflow.
        out.writeObject(node);

        System.out.println("done");
    }
}
公共类节点实现可序列化
{
私有ArrayList节点=新建ArrayList();
公共静态void main(字符串[]args)引发异常
{
节点=新节点();
int深度=3000;
//添加链接到指定深度的节点
Node last=节点;
for(int i=0;i

您需要减少图形的深度以限制递归序列化调用的数量,或者编写自定义序列化以解决此问题。自定义序列化在本质上需要是非递归的,不幸的是,乍一看,实现起来似乎并不简单。

我也遇到过类似的问题。经过多次搜寻,我发现了一个Kryo叉子,用来处理嵌套很深的物体。通过,克隆和
mvn清洁安装
。目前是
com.esotericsoftware.kryo:kryo:2.23-SNAPSHOT


(旁注:上面有很多问题可以用这个来回答。我应该发布答案的副本(比如),或者评论到这个答案的链接,或者将问题标记为指向这个答案的重复问题,或者什么?

我知道这已经晚了很多年,但我在试图解决这个问题时通过谷歌发现了这一点

我想要一个比编写自定义序列化代码所需的代码量少得多的解决方案

问题是节点连接到节点,而节点连接到节点。 网络的很大一部分可以从任何给定的节点到达——对于无向图,它是每个节点

当您尝试序列化一个节点时,每个可到达的节点都将在堆栈上结束

最简单的解决方案不是将节点直接连接到其邻居,而是添加一层间接连接:

class Node{

    private final Map<NodeRef, Connection> forwardConnections = new HashMap<>();
    private final Map<NodeRef, Connection> reverseConnections = new HashMap<>();

    ...
}

class Connection{

    private final NodeRef source;
    private final NodeRef dest;
    private final ConnectionMetadata meta;

    public Connection(Node source, Node dest, ConnectionMetadata meta){
        this.source = new NodeRef(source);
        this.dest = new NodeRef(dest);
    }

    public Node getSource(){
        return source.resolve();
    }

    public Node getDest(){
        return dest.resolve();
    }

    public ConnectionMetadata getMeta(){
        return meta;
    }

}

public class NodeRef{

    private transient Node node;
    private final Network network;
    private final int[] uid;

    public NodeRef(Node node){
        this.node = node;
        this.network = node.getNetwork();
        this.uid = node.getUID();
    }

    //Won't be called during deserialisation
    public Node resolve(){
        if(node == null){
            node = network.resolve(uid);
        }
        return node;
    }

    //Will be called when connections maps are deserialised.
    public boolean equals(Object o){
        //you might also want to check that the networks are equal
        return (o instanceof NodeRef) && Arrays.equals(uid, ((NodeRef)o).uid);
    }


    //Will be called when connections maps are deserialised.
    public int hashCode(){
        return Arrays.hashCode(uid);
    }
}
类节点{
private final Map forwardConnections=new HashMap();
private final Map reverseConnections=new HashMap();
...
}
类连接{
私有最终节点ref源;
私人最终目的地;
私有最终连接元数据元数据;
公共连接(节点源、节点目标、ConnectionMetadata){
this.source=新节点ref(source);
this.dest=新节点ref(dest);
}
公共节点getSource(){
返回source.resolve();
}
公共节点getDest(){
返回dest.resolve();
}
公共连接元数据getMeta(){
返回元;
}
}
公共类节点{
私有瞬态节点;
专用最终网络;
私有最终int[]uid;
公共节点ref(Node Node){
this.node=节点;
this.network=node.getNetwork();
this.uid=node.getUID();
}
//在反序列化期间不会调用
公共节点解析(){
if(node==null){
node=network.resolve(uid);
}
返回节点;
}
//将在反序列化连接映射时调用。
公共布尔等于(对象o){
//您可能还需要检查网络是否相等
返回(NodeRef的instanceof)和&array.equals(uid,((NodeRef)o.uid);
}
//将在反序列化连接映射时调用。
公共int hashCode(){
返回数组。hashCode(uid);
}
}

关于什么的递归?另外,编写一个自定义writeObject方法,并保证该方法中没有递归,这不会改变任何事情?如果它已经在内存中,我不认为问题在于对象的大小。这可能是真的,我不知道会发生这种情况。不过,我确实为顶点生成了一个
serialVersionUID
。我该如何避免这个问题呢?伙计们,序列化已经序列化的对象不会导致堆栈溢出。Java序列化句柄而不是对象。循环对象图可以序列化