【问题标题】:Java Garbage Collection DefragmentationJava 垃圾回收碎片整理
【发布时间】:2011-09-29 08:34:39
【问题描述】:

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

这是我的测试:

  • 使用参数 -Xms10M -Xmx10M 设置 java JVM
  • 我分配了一个占 JVM 内存 90% 的字节数组。
  • 然后将此字节数组设置为空,然后尝试重新分配占 JVM 内存 90% 的字节数组。
  • 我们从日志中看到 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

我正在运行的另一个 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);

    }

第二次测试的输出日志。 总可用内存:9881728 尝试分配大小数组:6917209 释放大小的分配数组:6917209 已用内存:7519744 -Iteration: 0 即将释放内存 -Iteration: 0 即将重新分配内存 -已用内存:7890928 -最大内存:10092544 - 空闲内存:2147808 -Iteration: 1 即将释放内存 -Iteration: 1 即将重新分配内存 -已用内存:930952 -最大内存:10092544 - 空闲内存:9107792 -Iteration: 2 即将释放内存 -Iteration: 2 即将重新分配内存 -已用内存:1181832 -最大内存:10092544 - 空闲内存:8856912 -Iteration: 3 即将释放内存 -Iteration: 3 即将重新分配内存 -已用内存:1190552 -最大内存:10092544 - 空闲内存:8848192 -Iteration: 4 即将释放内存 -Iteration: 4 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 -Iteration: 5 即将释放内存 -Iteration: 5 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 -Iteration: 6 即将释放内存 -Iteration: 6 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 -Iteration: 7 即将释放内存 -Iteration: 7 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 -Iteration: 8 即将释放内存 -Iteration: 8 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 -Iteration: 9 即将释放内存 -Iteration: 9 即将重新分配内存 -已用内存:902408 -最大内存:10092544 - 空闲内存:9190136 最后一次释放阵列 尝试分配大小数组:6917209 总可用内存:9956688 线程“主”java.lang.OutOfMemoryError 中的异常:Java 堆空间

【问题讨论】:

  • 你能展示一下Java代码吗?
  • 另一个注意事项,我使用的 CEE-J JVM 使用 Mark and Sweep 垃圾收集,这是否意味着 Java 堆没有被拆分为多个区域(代)或者拆分不同垃圾回收的类型?

标签: java memory jvm


【解决方案1】:

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

【讨论】:

  • 垃圾回收后这些区域可以改变大小吗?
【解决方案2】:

添加 -XX:MaxPermSize 参数以为堆提供足够的空间。

-XX:MaxPermSize=512m

【讨论】:

  • IIRC,Perm 用于代码相关的东西。我认为你想要“终身一代”。
【解决方案3】:

您在哪个其他 JVM 中成功运行了您的程序。如果您谈论的是 IBM jdk,那么堆管理与 Sun(Oracle)jvm 堆管理完全不同。 http://download.boulder.ibm.com/ibmdl/pub/software/dw/jdk/diagnosis/GCandMemory-042005.pdf

【讨论】:

  • 我正在运行的另一个 JVM 是 Skelmir 的 CEE-J,我在低内存设备上运行,因此无法设置 MaxPermSize。有趣的是,java堆被分成不同的区域,这些区域在垃圾收集后可以改变大小吗?我已经运行了另一个测试,
  • 在另一个 CEE-J 上面的第二个测试失败
猜你喜欢
  • 1970-01-01
  • 2014-02-23
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
相关资源
最近更新 更多