序列化期间堆空间不足

以下代码导致大约 300 万行。OutOfMemmoryError: heap space

分配给 JVM 的内存为 4 GB,使用 64 位安装。

while (rs.next())
{    
    ArrayList<String> arrayList = new ArrayList<String>();
    for (int i = 1; i <= columnCount; i++)
    {
        arrayList.add(rs.getString(i));
    }

    objOS.writeObject(arrayList);
}

在 while 循环的每次迭代中,引用的内存都有资格进行垃圾回收,并且在内部 JVM 在抛出堆空间之前调用垃圾回收 ()。ArrayListSystem.gc()OutOfMemoryError

那么,为什么会发生异常呢?


答案 1

是 ?objOSObjectOutputStream

如果是这样,那么这就是你的问题:An保留了对曾经写入它的每个对象的强引用,以避免将同一个对象写入两次(它将简单地写一个引用,说“我之前用id x写的那个对象”)。ObjectOutputStream

这意味着您有效地泄漏了所有空间。ArrayList

您可以通过在 上调用 reset() 来重置该“缓存”。由于您似乎无论如何都不会在调用之间使用该缓存,因此您可以在调用后直接调用。ObjectOutputStreamwriteObjectreset()writeObject()


答案 2

我同意@Joachim。

下面的建议是一个神话

此外,建议(在良好的编码约定中)不要在循环内声明任何对象。相反,请在循环开始之前声明它,并使用相同的引用进行初始化。这将要求你的代码对每次迭代使用相同的引用,并减少内存释放线程(即垃圾回收)的负担。

真相
编辑了这个,因为我觉得可能有很多人(像今天之前的我一样)仍然认为在循环中声明对象可能会损害内存管理;这是错误的。
为了演示这一点,我使用了stackOverflow上发布的相同代码。
以下是我的代码片段

package navsoft.advskill.test;

import java.util.ArrayList;

public class MemoryTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        /* Total number of processors or cores available to the JVM */
        System.out.println("Available processors (cores): "
                + Runtime.getRuntime().availableProcessors());
        /*
         * Total amount of free memory available to the JVM
         */
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("Free memory (bytes): "
                + freeMemory);
        /*
         * This will return Long.MAX_VALUE if there is no preset limit
         */
        long maxMemory = Runtime.getRuntime().maxMemory();
        /*
         * Maximum amount of memory the JVM will attempt to use
         */
        System.out.println("Maximum memory (bytes): "
                + (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
        /*
         * Total memory currently in use by the JVM
         */
        System.out.println("Total memory (bytes): "
                + Runtime.getRuntime().totalMemory());
        final int LIMIT_COUNTER = 1000000;
        
        //System.out.println("Testing Only for print...");
        System.out.println("Testing for Collection inside Loop...");
        //System.out.println("Testing for Collection outside Loop...");
        //ArrayList<String> arr;
        for (int i = 0; i < LIMIT_COUNTER; ++i) {
            //arr = new ArrayList<String>();
            ArrayList<String> arr = new ArrayList<String>();
            System.out.println("" + i + ". Occupied(OldFree - currentFree): "+ (freeMemory - Runtime.getRuntime().freeMemory()));
        }
        System.out.println("Occupied At the End: "+ (freeMemory - Runtime.getRuntime().freeMemory()));
        System.out.println("End of Test");
    }

}

输出的结果清楚地表明,如果在循环内部或外部声明对象,则在占用/释放内存方面没有区别。因此,建议将声明的范围尽可能小。
我感谢StackOverflow(特别是@Miserable Variable)的所有专家指导我。

希望这也能消除您的疑虑。