当前位置: 首页 > 面试题库 >

根据JVM的内存粒度确定阵列的最佳大小

汝志
2023-03-14
问题内容

当为(例如)一个集合创建支持数组时,您实际上并不在乎所创建数组的确切大小,它只需要至少与您计算的大小一样大即可。

但是由于有了内存分配和VM的数组标头,在某些情况下就可以创建更大的数组而不消耗更多的内存-对于Oracle
32位VM(至少就是互联网上的几个来源所声称的),内存粒度为8(意味着任何内存分配都将舍入到下一个8字节边界),并且数组头的开销为12字节。

这意味着在分配Object [2]时,应该消耗20个字节(12 + 2 * 4),但是由于粒度的原因,实际上将占用24个字节。以相同的内存成本创建Object
[3]是可能的,这意味着一个集合将不得不稍后再调整其支持数组的大小。相同的原理可以应用于原始数组,例如,用于I / O缓冲区的byte
[],字符串生成器中的char []等。

尽管这样的优化不会真正产生明显的效果,除非在最极端的情况下,调用静态方法来“优化”数组大小不会有太大的麻烦。

问题是,在JDK中没有这样的“到内存粒度为止的舍入数组大小”。我自己编写这种方法将需要确定VM的一些关键参数:内存粒度,数组标头开销以及每种类型的大小(主要是引用问题,因为它们的大小会随体系结构和VM选项而变化)。

那么,有没有一种方法可以确定这些参数,或者通过其他方式实现所需的“舍入”?


问题答案:

有趣的主意。我认为确定此问题的更便捷的方法是实际衡量使用情况。示例程序:

public class FindMemoryUsage {
    public static void main(String[] args) {
        for (int i=0; i<50; i+=2) {
            long actual = getActualUsageForN(i);
            System.out.println(i + " = " + actual);
            long theoretical = getTheoreticalUsageForN(i);
            if (theoretical != actual) {
                throw new RuntimeException("Uh oh! Mismatch!");
            }
        }
    }

    private static long getTheoreticalUsageForN(long count) {
        long optimal = (Unsafe.ARRAY_BYTE_BASE_OFFSET + Unsafe.ARRAY_BYTE_INDEX_SCALE * count);
        return ((optimal - 1) & ~7) + 8;
    }

    private static long getActualUsageForN(int count) {
        System.gc();
        byte[][] arrays = new byte[3000000][];
        long begin = usedMemory();
        for (int i=0; i<arrays.length; i++) {
            arrays[i] = new byte[count];
        }
        long end = usedMemory();
        return Math.round((end - begin) / (double) arrays.length);
    }

    private static long usedMemory() {
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    }
}

该程序为您提供此信息:

0 = 16
2 = 16
4 = 16
6 = 24
8 = 24
10 = 24
12 = 24
14 = 32
16 = 32
18 = 32
20 = 32
22 = 40
24 = 40
26 = 40
28 = 40
30 = 48
32 = 48
34 = 48
36 = 48
38 = 56
40 = 56
42 = 56
44 = 56
46 = 64
48 = 64

该数据来自使用率的实际计算和基于sun.misc.Unsafe的常数和8字节舍入的理论使用率。这意味着您可以像建议的那样使用这些常量来“四舍五入”:

private static int roundSizeUp(int from) {
    long size = (Unsafe.ARRAY_BYTE_BASE_OFFSET + Unsafe.ARRAY_BYTE_INDEX_SCALE * from);
    long actual = ((size - 1) & ~7) + 8;
    return (int) (actual - Unsafe.ARRAY_BYTE_BASE_OFFSET) / Unsafe.ARRAY_BYTE_INDEX_SCALE;
}

这是特定于VM的代码,但是getActualUsageForN如果需要更多的可移植性,您可能可以根据该策略找到如何执行此操作。

请注意,这不是生产质量的代码:您需要仔细考虑溢出,并将Unsafe引用更改为实际上适用于所使用数组类型的常量。



 类似资料:
  • 我正在考虑这个leetcode问题,在完成这个天真的方法时遇到了一个问题。我在这里找到了一个最佳的解决方案。但我不确定我天真的尝试到底出了什么问题。 问题如下: 给定两个整数数组A和B,返回两个数组中出现的子数组的最大长度。 示例: 输入:A:[1,2,3,2,1]B:[3,2,1,4,7] 输出:3 说明:最大长度的重复子数组为[3,2,1]。 这是我当前的代码: 我的解决方案通过了几个测试用例

  • 我使用的计算机规格如下:OS-Windows7 professional Installed Memory(RAM):8Gb系统类型:64位操作系统JVM:Java version 8 update 91(jre1.8.0_91)64位版本 Java版本"1.8.0_91"Java(TM)SE运行时环境(内部版本1.8.0_91-b15)JavaHotSpot(TM)64位服务器VM(内部版本25

  • 本文向大家介绍jvm最大内存限制多少?相关面试题,主要包含被问及jvm最大内存限制多少?时的应答技巧和注意事项,需要的朋友参考一下 考察点:JVM (1)堆内存分配 JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小 于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到

  • 我使用最大堆来查找数组中的k个最大元素,如下所示: 1) 我已经构建了给定数组的前k个元素(arr[0]到arr[k-1])的最小堆MH。O(k) 2)对于每个元素,在第k个元素(arr[k]到arr[n-1])之后,将其与MH的根进行比较。 ……a)如果元素大于根,则将其设为根,并为MH调用heapify ……b)否则忽略它。 //步骤2是O((n-k)) 3)最后,MH有k个最大元素,MH的根

  • 给定一个2维正整数数组,求和最大的HxW子矩形。矩形的总和是该矩形中所有元素的总和。 输入:具有正元素的二维数组NxN子矩形的HxW大小 输出:HxW大小的子矩阵,其元素的总和最大。 我已经使用蛮力方法解决了这个问题,但是,我现在正在寻找一个具有更好复杂性的更好的解决方案(我的蛮力法的复杂性是O(n6))。

  • 问题内容: 我试图确定双精度的最大精度是多少。在此链接的可接受答案的注释中,Java中的double保持精度 @PeterLawrey将max precision设置为15。 您如何确定呢? 问题答案: @PeterLawrey表示最大精度为15。 实际上,这根本不是他所说的。他说的是: 双精度有15个小数位 他错了。它们的精度为15个十进制 数字 。 任何数字中的小数位数由其对数10的对数给出。