zoukankan      html  css  js  c++  java
  • 数据结构之Java对象内存

    前言:

          Java最重要的特征之一就是它的内存分配系统,我们在分析Java对象的内存的时候,可以将它分解为基本的原始数据类型,而原始数据类型在Java中占用的大小是预先定义好的。只需要将变量的数量和他们预先定义好的字节数相乘即可。,下图是Java中原始数据类型的常见内存需求。

    但是因为内存的使用和具体的机器有关,这里为了方便描述,统一采用64位机器,即表示机器地址需要8字节。

    对象:

    要直到一个对象使用的内存量需要将所有变量使用的内存与对象本身的开销(一般占用16字节)相加。这些开销包括指向对象的类的引用、垃圾搜集信息和同步信息。因为是在64位机器上,所以内存使用一般都为被填充为8字节的倍数,例如:一个Integer类型占用24字节,其中16字节对象开销,4字节int值以及4字节填充。

    如图:

      public class Integer

    {  private int x;

       ……

    }

    16字节对象开销

    4字节 int值

    4字节填充

     类似的,一个Date对象:

      public class Date

    {   private int day;

        private int month;

    private int year;

    ……

    }

    16字节对象开销

    4字节 int值

    day

    4字节 int值

    month

    4字节 int值

    year

    4字节填充

    32字节

    引用指向的对象所占用的内存我们需要单独说明,所以这个对象里并没有包含实际的string对象占用的内存。

    链表:

    我们以前面介绍的内部类Node作为例子,这里还需要额外的8字节指向外部类的引用。因此,一个Node类需要16个字节的对象开销,8字节指向外部类,8字节指向T类型对象的引用,8字节指向下一个Node对象的引用,总共40字节。当T类型为Integer类型时,因为Integer需要24字节,所以含有N个整数的链表需要(32+64N)字节,包括Stack对象的16字节开销,引用实例类型8字节,int4字节。如图:

    相关代码:

      public class Stack<T>

    {

        private Node first;

        private int n;

        private class Node<T>

    {

          private Node next;

           private T t;

    }

    }

    Stack:

    16字节的对象开销

    8字节Node的引用

    4字节int

    Node:

    16字节的对象开销

    8字节指向Node的引用

    8字节指向T类型的引用

    8字节指向外部类的引用

    Integer:

    16字节对象开销

    4字节 int值

    4字节填充

    数组:

    Java中数组被实现为对象,他们需要额外的内存来记录长度。因此一般需要24字节的头信息,包括16字节的对象开销,4字节记录长度,4字节填充。例如一个含有N个double的数组需要(24+8N)字节,含有N个int的数组需要(24+4N)字节(会被填充为8字节的倍数),如果是一个对象数组,实际上数组里存的是对象的引用。例如一个含有N个Date对象的数组需要24字节+8N字节的引用+每个 对象占用32字节,总共(24+8N+32N)字节。

    上面的例子如下图:

    int[] ary=new int[N];

    16字节的对象开销

    4字节的长度记录

    4字节填充

    4字节int

    4字节int

    4字节int

    ……

    4字节int

    4字节int

    4字节int

    注:实际大小会被填充为8字节的倍数

    double[] ary=new double[N];

    16字节的对象开销

    4字节的长度记录

    4字节填充

    8字节double

    8字节double

    8字节double

    ……

    8字节double

    8字节double

    8字节double

    Date[] ary=new Date[N];

    16字节的对象开销

    4字节的长度记录

    4字节填充

    8字节Date引用

    8字节Date引用

    8字节Date引用

    ……

    8字节Date引用

    8字节Date引用

    8字节Date引用

     第一个date

    16字节对象开销

    4字节 int值

    day

    4字节 int值

    month

    4字节 int值

    year

    4字节填充

      第二个date

    16字节对象开销

    4字节 int值

    day

    4字节 int值

    month

    4字节 int值

    year

    4字节填充

      ......

       第N个date

    16字节对象开销

    4字节 int值

    day

    4字节 int值

    month

    4字节 int值

    year

    4字节填充

    二维数组:

       实际上是数组的数组。如图:  

    double[][] ary=new double[M][N]

    总共M个数组引用:

    16字节的对象开销

    4字节的长度记录

    4字节填充

    8字节数组引用

    8字节数组引用

    8字节数组引用

    ……

    8字节数组引用

    8字节数组引用

    8字节数组引用

    16字节的对象开销

    4字节的长度记录

    4字节填充

    8字节double

    8字节double

    8字节double

    ……

    8字节double

    8字节double

    8字节double

    总共N个Double

    所以一共需要24(数组的数组的开销)+8M(所有元素数组的引用)+24M(所有数组元素的开销)+8MN(M个长度为N的double类型的数组)字节,对象的二维数组和这类似。

    字符串:

    Java中String的标准实现含有4个实例变量,一个指向字符数组的引用(8字节 ),剩下3个int值(4字节),一个描述字符数组中的偏移量,一个计数器来保存字符串的长度,最后一个保存该String对象的哈希值,在某些情况下可以省略计算。因此,一个一个String对象会使用16(对象开销)+4(int)+4(int )+4(int )+8(指向数组的引用)+4(填充)共40字节。这些是除了字符数组外字符长所需要的内存空间,所有字符需要的内存另计,因为String的char数组很多时候是共享的(String为不可变)。

    16字节的对象开销

    4字节int

    4字节int

    4字节int

    8字节指向数组引用

    4字节填充

    一个长度为N的String对象一般需要40字节加上24+2N字节(字符数组),但是在字符串中经常需要用到子串,所以Java对字符串的表示希望能避免复制字符串中的数组,例如,当调用String的substring()方法时,就创建了一个新的String对象(40字节),但是它仍然重用了相同的数组。因此该字符串的子串只会使用40字节,该子串保存一个原始字符数组的别名,并且它的偏移量和长度域标记了子字符串的位置。换句话说,一个子字符串需要的内存是一个常数,构造一个子串需要的时间也是一个常数。(这是1.6所采用的方法,但是因为会造成内存泄露,1.7后substring方法会在堆内存中创建一个新的数组。)

  • 相关阅读:
    WinDbg 查看静态变量
    PLSQL 安装说明
    WinDbg设置托管进程断点
    SQL Server 数据库备份策略,第一周运行失败的原因
    Eclipse开发C/C++ 安装配置图文详解
    C 语言静态链表实现
    C语言结构体,C语言结构体指针,java对象引用,传值,传地址,传引用
    C Primer Plus 收官二叉搜索树实现
    GDB 调试C程序
    C语言 结构体存储空间分配
  • 原文地址:https://www.cnblogs.com/lls101/p/11214781.html
Copyright © 2011-2022 走看看