我有一个常规的64位热点JVM,其堆栈大小为1 MB.现在我试图序列化一个具有3022个父级的层次结构的对象,这给了我SO(反讽)异常.
这是一些代码:
while(epc.getParent()!=null){ epc=epc.getParent(); count++; } print(count);//3022
上面的代码只是告诉层次结构,但当我尝试将epc
对象序列化到ObjectOutputStream 时,会发生实际问题.
问题,JVM中1 MB堆栈大小的状态是什么,因为我不知道堆栈帧的大小是多少?我确定每个堆栈帧不是1KB,因为我在-Xss3000k成功运行了代码.
还有一个问题,如果我放置-Xss3000k的JVM选项,每个线程的堆栈大小是否为3000k?
问题,JVM中1 MB堆栈大小的状态是什么,因为我不知道堆栈帧的大小是多少?
1 MB默认线程堆栈大小意味着每个线程默认具有1MB(1048576字节)的堆栈空间.例外情况是,如果您的代码使用其中一个Thread
构造函数创建一个线程,您可以在其中提供堆栈大小参数.
堆栈帧的大小取决于被调用的方法.它需要保存方法的参数和局部变量,因此帧大小取决于它们的大小.每个帧还需要(我认为)两个额外的单词来保存已保存的帧指针和保存的返回地址.
请注意,在递归算法中,您可以为一个"递归级别"拥有多个堆栈帧.对于writeObject
(在Java 8中),使用的算法是递归的,并且每个级别的数据结构通常有4个帧被序列化:
writeObject0 writeOrdinaryObject writeSerialData defaultWriteFields writeObject0 etcetera
由于编译器的差异以及ObjectInputStream/ObjectOutputStream的实现的变化,实际的帧大小将取决于平台.您最好尝试(粗略地)测量所需的堆栈空间,而不是尝试从第一原理预测帧大小.
还有一个问题,如果我放置-Xss3000k的JVM选项,每个线程的堆栈大小是否为3000k?
是的......除了我上面描述的例外.
解决您的困境的一个可能的解决方案是创建一个特殊的线程,其中包含一个用于序列化的大堆栈.反序列化将需要具有大堆栈的类似线程.对于其余的线程,默认的堆栈大小应该没问题.
其他可能的方案:
实现writeReplace
和readResolve
方法将epc对象的父结构展平为一个数组,这样就不会得到深度递归.(显然,需要非递归地完成展平/不展平.)
在你打电话之前做同样的扁平化writeObject
等等.
使用不同的序列化机制,或者可能是自定义机制.