内存中字节的大小 - Java

时间:2008-10-23 14:16:35

标签: java performance memory

我听说过一个字节在java程序中占用的内存量的混合意见。

我知道你可以在java字节中存储不超过+127,并且documentation表示一个字节只有8位但是here我被告知它实际上占用了相同的作为int的内存量,因此只是一个有助于代码理解而不是效率的类型。

任何人都可以清除这一点,这会是一个特定于实现的问题吗?

13 个答案:

答案 0 :(得分:60)

好的,有很多讨论而不是很多代码:)

这是一个快速的基准。当涉及到这种事情时,它有正常的警告 - 由于JITting等测试记忆有奇怪之处,但是对于适当大的数字它仍然是有用的。它有两种类型,每种类型有80个成员--LotsOfBytes有80个字节,LotsOfInts有80个int。我们构建了很多,确保它们不是GC,并检查内存使用情况:

class LotsOfBytes
{
    byte a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    byte b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    byte c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    byte d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    byte e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}

class LotsOfInts
{
    int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}


public class Test
{
    private static final int SIZE = 1000000;

    public static void main(String[] args) throws Exception
    {        
        LotsOfBytes[] first = new LotsOfBytes[SIZE];
        LotsOfInts[] second = new LotsOfInts[SIZE];

        System.gc();
        long startMem = getMemory();

        for (int i=0; i < SIZE; i++)
        {
            first[i] = new LotsOfBytes();
        }

        System.gc();
        long endMem = getMemory();

        System.out.println ("Size for LotsOfBytes: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));

        System.gc();
        startMem = getMemory();
        for (int i=0; i < SIZE; i++)
        {
            second[i] = new LotsOfInts();
        }
        System.gc();
        endMem = getMemory();

        System.out.println ("Size for LotsOfInts: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));

        // Make sure nothing gets collected
        long total = 0;
        for (int i=0; i < SIZE; i++)
        {
            total += first[i].a0 + second[i].a0;
        }
        System.out.println(total);
    }

    private static long getMemory()
    {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
}

我的盒子上的输出:

Size for LotsOfBytes: 88811688
Average size: 88.811688
Size for LotsOfInts: 327076360
Average size: 327.07636
0

显然有一些开销 - 它的外观有8个字节,虽然LotsOfInts只有7个字节(就像我说的那样,这里有奇怪的东西) - 但重点是字节字段似乎被打包为LotsOfBytes这样它(在删除开销之后)只占LotsOfInts的四分之一。

答案 1 :(得分:18)

是的,Java中的字节变量实际上是内存中的4个字节。然而,对于数组而言,这并不适用。存储20字节的字节数组实际上只有20字节的存储器。

这是因为Java字节码语言只知道两种整数类型:int和long。所以它必须在内部处理所有数字作为任何一种类型,这些类型在内存中是4和8字节。

但是,Java知道每个整数格式的数组。因此,短数组的存储实际上是每个条目两个字节,每个条目一个字节用于字节数组。

我一直说“存储”的原因是数组也是Java中的一个对象,并且每个对象本身都需要多个字节的存储空间,而不管实例变量或数组存储的存储情况如何数组需要。

答案 2 :(得分:7)

Java永远不是实现或平台特定的(至少就primitive type sizes而言)。无论您使用什么平台,它们始终保证原始类型保持不变。这与C和C ++不同(并且被认为是一种改进),其中一些原始类型是特定于平台的。

由于底层操作系统一次更快地处理四个(或八个,64位系统)字节,因此JVM可以分配更多字节来存储一个原始字节,但是你仍然只能存储来自-128到127。

答案 3 :(得分:5)

一个显而易见的练习是在一些使用字节和整数执行简单操作的代码上运行javap。你会看到字节码,它们希望int参数在字节上运行,并且字节码被插入以便从一个字节到另一个字符串。

请注意,虽然字节数组不会存储为4字节值的数组,但1024字节的字节数组将使用1k内存(忽略任何开销)。

答案 4 :(得分:5)

我使用http://code.google.com/p/memory-measurer/进行了测试 请注意,我使用的是64位Oracle / Sun Java 6,没有任何参考压缩等。

每个对象占用一些空间,加上JVM需要知道该对象的地址,“地址”本身是8个字节。

对于基元,看起来像基元被转换为64位以获得更好的性能(当然!):

byte: 16 bytes,
 int: 16 bytes,
long: 24 bytes.

使用数组:

byte[1]: 24 bytes
 int[1]: 24 bytes
long[1]: 24 bytes

byte[2]: 24 bytes
 int[2]: 24 bytes
long[2]: 32 bytes

byte[4]: 24 bytes
 int[4]: 32 bytes
long[4]: 48 bytes

byte[8]: 24 bytes => 8 bytes, "start" address, "end" address => 8 + 8 + 8 bytes
 int[8]: 48 bytes => 8 integers (4 bytes each), "start" address, "end" address => 8*4 + 8 + 8 bytes
long[8]: 80 bytes => 8 longs (8 bytes each), "start" address, "end" address => 8x8 + 8 + 8 bytes

现在猜猜是什么......

    byte[8]: 24 bytes
 byte[1][8]: 48 bytes
   byte[64]: 80 bytes
 byte[8][8]: 240 bytes

P.S。 Oracle Java 6,最新最好的,64位,1.6.0_37,MacOS X

答案 5 :(得分:3)

这取决于JVM如何应用填充等。一个字节数组(在任何理智的系统中)将打包成每个元素1个字节,但是一个具有四个字节字段的类可以紧密打包或填充到字边界 - 它依赖于实现。

答案 6 :(得分:2)

你被告知的是完全正确的。 Java字节代码规范只有4字节类型和8字节类型。

byte,char,int,short,boolean,float都存储在每个4个字节中。

double和long以8个字节存储。

然而字节代码只是故事的一半。还有JVM,它是特定于实现的。 Java字节代码中有足够的信息来确定变量被声明为一个字节。 JVM实现者可能决定只使用一个字节,尽管我认为这种可能性很小。

答案 7 :(得分:2)

您可以随时使用long并将数据打包在自己身上以提高效率。然后你总是可以保证你将使用所有4个字节。

答案 8 :(得分:2)

byte = 8bit = Java Spec定义的一个字节。

字节数组需要多少内存定义 ,也不定义复杂对象需要多少内存。

对于Sun JVM,我记录了规则:https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/5163

答案 9 :(得分:0)

在我的网站上查看我的MonitoringTools(www.csd.uoc.gr/~andreou)

class X {
   byte b1, b2, b3...;
}

long memoryUsed = MemoryMeasurer.measure(new X());

(它也可以用于更复杂的对象/对象图)

在Sun的1.6 JDK中,似乎一个字节确实需要一个字节(在旧版本中,就内存而言是int~byte)。但请注意,即使在旧版本中,byte []也被打包为每个条目一个字节。

无论如何,重点是不需要像Jon Skeet这样的复杂测试,只能给出估计。我们可以直接测量物体的大小!

答案 10 :(得分:0)

通过阅读上述评论,似乎我的结论对许多人来说都是一个惊喜(这对我来说也是一个惊喜),所以值得重复:

  • 变量的旧大小(int)== size(byte)不再包含,至少在Sun的Java 6中是这样。

相反,size(byte)== 1 byte(!!)

答案 11 :(得分:0)

只想指出声明

您可以在java字节中存储不超过+127

不是真的正确。

您总是可以在一个字节中存储256个不同的值,因此您可以轻松地将0..255范围设置为“无符号”字节。

这一切都取决于你如何处理这8位。

示例:

byte B=(byte)200;//B contains 200
System.out.println((B+256)%256);//Prints 200
System.out.println(B&0xFF);//Prints 200

答案 12 :(得分:-3)

似乎答案可能取决于您的JVM版本,也可能取决于您运行的CPU架构。 Intel系列CPU有效地进行字节操作(由于其8位CPU历史记录)。一些RISC芯片需要对多个操作进行字(4字节)对齐。对于堆栈上的变量,类中的字段和数组中的内存分配可能不同。