Java垃圾收集碎片整理

mad*_*lad 9 java memory jvm

我在分配和释放字节数组时遇到OutOfMemoryError java堆异常,即使有足够的可用内存用于分配.下面有一个简短的日志.我知道从不同的JVM上运行,问题是由于内存碎片造成的,最大的空闲块只有大约6MB.我认为java(oracle)JVM应该处理碎片化的内存.

这是我的测试:

  • 使用params -Xms10M -Xmx10M设置java JVM
  • 我分配一个字节数组,比如说JVM内存的90%.
  • 将此字节数组设置为null,然后尝试重新分配90%JVM内存的字节数组.
  • 我们从日志中看到JVM内存已经重置为全部,但是我们无法分配相同数量的内存.
  • 因此,内存必须碎片化?

这是我的详细信息:

Total Free Memory: 9881728  
Attempting to Allocate Array of size: 6917209  
Freeing Allocated Array of size: 6917209  
Total Free Memory: 9881728  
Attempting to Allocate Array of size: 6917209  
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Run Code Online (Sandbox Code Playgroud)

我正在运行的另一个JVM是Skelmir的CEE-J,我在低内存设备上运行,所以我无法设置MaxPermSize.有趣的是,java堆被拆分成不同的区域,这些区域可以在垃圾收集后改变大小吗?我已经运行了另外的测试我在哪里,首先为大约95%的内存创建一个字节数组,然后再次释放数组,然后我创建了一个包含散列图的对象数组,每个散列图都有一个条目,我清除了数组和映射,然后再次重新分配相同的数组.

我每次分配和释放时检查内存.这样做了10次之后,我最后一次释放内存,然后我检查内存,看到我有完整的JVM分配,然后我尝试再次分配95%的JVM分配并获得相同的JVM内存异常,即使它说我有足够的内存来分配?

public static void main(String[] args) throws InterruptedException {
        Runtime s_runtime = Runtime.getRuntime ();
        long used_memory = s_runtime.totalMemory () - s_runtime.freeMemory ();
        long start_memory = used_memory;
        long freeMemory = s_runtime.freeMemory();




        Integer numSubMemories = 1;
        Integer numMapEntries = 1;
        Integer numIterations = 1;
        float percentMem = (float) 1.0;
        if(args.length>0){
            if(args[0]!=null){
                String subEntries = args[0];
                if(subEntries.length()>0){
                    numSubMemories = Integer.parseInt(subEntries);
                }
            }
            if(args[1]!=null){
                String mapEntries = args[1];
                if(mapEntries.length()>0){
                    numMapEntries = Integer.parseInt(mapEntries);
                }
            }
            if(args[2]!=null){
                String numIts = args[2];
                if(numIts.length()>0){
                    numIterations = Integer.parseInt(numIts);
                }
            }
            if(args[3]!=null){
                String percentMemArg = args[3];
                if(percentMemArg.length()>0){
                    percentMem = Float.parseFloat(percentMemArg);
                }
            }
        }

        System.out.println("Total Free Memory: " + freeMemory);
        int numBytes = (int) (freeMemory * percentMem);
        System.out.println("Attempting to Allocate Array of size: " + numBytes);
        byte[] mbyteArray = new byte[numBytes];
        System.out.println("Freeing Allocated Array of size: " + numBytes);
        mbyteArray = null;



        TestMemory myMemory = new TestMemory(numSubMemories,numMapEntries);
        used_memory = s_runtime.totalMemory () - s_runtime.freeMemory ();
        System.out.println("Used Memory: " + used_memory);
        long after_allocation_memory = used_memory;
        for(int i=0;i<numIterations;i++){
            System.out.println("\t\t--Iteration: "+ i + " about to release Memory");
            myMemory.releaseMemoryArray(numSubMemories);

            System.out.println("\t\t--Iteration: "+ i + " about to reallocate Memory");
            myMemory.reAllocateMemoryArray(numSubMemories,numMapEntries);
            used_memory = s_runtime.totalMemory () - s_runtime.freeMemory ();
            System.out.println("\t\t--Used Memory: " + used_memory);
            System.out.println("\t\t--Max Memory: " + s_runtime.maxMemory());
            System.out.println("\t\t--Free Memory: " + s_runtime.freeMemory());
            System.gc();
            System.out.flush();
        }
        System.out.println("Releasing the Arrays for the last time");
         myMemory.releaseMemoryArray(numSubMemories);
         System.gc();
         freeMemory = s_runtime.freeMemory();
         //numBytes = (int)(freeMemory*0.95);
         System.out.println("Attempting to Allocate Array of size: " + numBytes);
         System.out.println("Total Free Memory: " + freeMemory);
         mbyteArray = new byte[numBytes];
        long end_memory = used_memory;
        //System.gc();
        Thread.sleep(6000);
        used_memory = s_runtime.totalMemory () - s_runtime.freeMemory ();

        System.out.println("------Results---------");
        System.out.println("        Start Memory: " + start_memory);
        System.out.println("        After Memory: " + after_allocation_memory);
        System.out.println("         End  Memory: " + end_memory);
        System.out.println("End Release   Memory: " + used_memory);

    }
Run Code Online (Sandbox Code Playgroud)

从第二次测试输出日志.总可用内存:9881728尝试分配大小数组:6917209释放已分配大小的数组:6917209已用内存:7519744 - 迭代:0即将释放内存 - 迭代:0即将重新分配内存 - 使用内存:7890928 -Max内存:10092544 -Free Memory:2147808 -Iteration:1即将发布内存-Iteration:1即将重新分配内存 - 使用内存:930952 -Max内存:10092544 -Free内存:9107792 -Iteration:2即将发布内存-Iteration:2即将发布重新分配内存使用内存:1181832 -Max内存:10092544 -Free内存:8856912 -Iteration:3即将释放内存-Iteration:3即将重新分配内存-Used内存:1190552 -Max内存:10092544 -Free内存:8848192 -Iteration :4即将释放内存 - 迭代:4即将重新分配内存 - 使用内存:902408 -Max内存:10092544 -Free内存:9190136 -Iteration:5即将释放内存-Iteration:5即将重新分配内存-Used Memory:902408 -Max Memory:10092544 -Free Memory:919013 6 - 迭代:6即将释放内存 - 迭代:6即将重新分配内存 - 使用内存:902408 -Max内存:10092544 -Free内存:9190136 -Iteration:7即将释放内存-Iteration:7即将重新分配内存-Used内存:902408 -Max内存:10092544 -Free内存:9190136 -Iteration:8即将释放内存-Iteration:8即将重新分配内存-Used Memory:902408 -Max内存:10092544 -Free内存:9190136 -Iteration:9即将发布release Memory -Iteration:9即将重新分配内存 - 使用内存:902408 -Max内存:10092544 -Free内存:9190136最后一次释放数组尝试分配大小的数组:6917209总可用内存:9956688线程中的异常"main "java.lang.OutOfMemoryError:Java堆空间

Tom*_*ine 3

Java 堆被分为多个区域。单个分配不能跨越区域。Oracle JRE 中有多种-XX允许配置分配的选项 [我将让其他人指出详细信息]。您可以将伊甸园和幸存者区域设置得非常小。另一个问题是,如果收集后可用内存不足,您可能仍然会收到 OOME 来防止抖动(再次有更多-XX选项来控制)。