不同语言的数组 - 存储引用或原始对象?

时间:2015-09-03 17:14:44

标签: java c arrays jvm

在使用数组时,我试图围绕原始内存在不同语言中的样子。

考虑以下Java代码:

String a = "hi";
String b = "there";
String c = "everyone";
String[] array = {a, b, c};

显然数组是持有引用,而不是对象;也就是说,在三个引用的内存中存在一个连续的数组,每个引用指向对象所在的内存中的某个其他位置。因此,物体本身不一定坐在三个连续的桶中;相反,引用是。

现在考虑一下:

String[] array = {"hi", "there", "everyone"}

我想在这种情况下,字符串存在于内存中的所有其他常量,然后数组保存对内存中这些常量的引用?因此,在原始内存中,数组看起来不像['h', 'i', '\0', 't', 'h', 'e', 'r', 'e'... (etc)]。 (为方便起见,使用c风格的终止)。相反,它更像是['a83a3edf' ,'a38decd' ... (etc)],其中每个元素都是一个内存位置(引用)。

我从这个思考过程得出的结论是,在Java中,你永远不会将数组想象成内存中连续对象的桶,而是连续的引用。我想不出有什么方法可以保证对象总是在Java中连续存储。

现在考虑C:

char *a = "hi";
char *b = "there";
char *c = "everyone";
char *array[] = {a, b, c};

上面的代码在功能上等同于上面的Java - 也就是说,数组保存对其他内存位置的引用(指针)。与Java一样,指向的对象不一定是连续的。

但是,在以下C代码中:

struct my_struct array[5];  // allocates 5 * size(my_struct) in memory! NOT room for 5
                            // references/pointers, but room for 5 my_structs.

array中的结构区域连续位于原始内存中。

现在提出具体问题:

  1. 我是否认为在Java中,数组必须始终保持引用,因为程序员只能访问Java中的引用?原始数据类型怎么样?它会以不同的方式工作吗? Java中的int数组是否会像原始内存中的C一样(除了Object类Java将添加)?

  2. 在Java中,程序员是否无法保证对象的连续内存分配?它可能偶然发生,或者概率很高,但程序员不能保证会这样吗?

  3. 在C中,程序员可以在内存中连续创建对象(结构)的原始数组,如上所示,是正确的吗?

  4. 其他语言如何处理?我猜Python就像Java一样工作?

  5. 这个问题的动机是我想要用这些语言中的数组来理解原始内存级别的情况。主要是程序员面试问题。我在之前的一次采访中说过,一个数组(不是任何语言,一般而言)都会像桶一样在内存中连续存放对象。只有在我说完之后,才意识到它并不像Java这样的语言。所以我想100%清楚它。

    感谢。如果有任何需要澄清,请告诉我。

4 个答案:

答案 0 :(得分:6)

  

你永远不会把数组想象成内存中连续对象的桶,而是连续的引用。

理论上你是对的,在实践中,JVM不会随机化内存访问。它按顺序分配内存,并在GC期间按发现顺序(或反向顺序)复制对象

  

我是否认为在Java中,数组必须始终保持引用,因为程序员只能访问Java中的引用?

是的,除非你当然有一系列基元。

  

原始数据类型怎么样?它会以不同的方式工作吗?

原语和引用在内存中是连续的。它们基本相同。

  

Java中的一组int将看起来就像原始内存中的C一样(除了Object类cruft Java将添加)?

  

在Java中,程序员是否无法保证对象的连续内存分配?

除非您使用关闭堆内存。虽然通常情况下这并不像你在大多数情况下认为的那样,但对象在内存中会是连续的。

  

它可能偶然发生,或者概率很高,但程序员不能保证会这样吗?

正确。通常,当您观察最差0.1%或更高的延迟时,您会遇到更大的问题。

  

在C中,程序员可以在内存中连续创建对象(结构)的原始数组,如上所示,是正确的吗?

是肯定的。您也可以在Java中执行此操作,但必须使用off堆内存。有许多库支持这一点,如Javolution,Chronicle,SBE。

答案 1 :(得分:0)

像C这样的低级语言会让你处理内存布局,以及你是否有指向其他地方的指针或者这里的值。确保正确处理堆栈与堆分配,并且不要忘记free()每个指针malloc()

Java,Python和JavaScript等高级语言会剥夺内存的低级布局。所有对象都在堆上,并且您具有对它的引用。虽然引用类似于指针,但它是不透明的,并且不直接与给定的内存位置相关联。因此,所有数据结构都包含对对象的引用。

答案 2 :(得分:0)

到1)在java数组中,对象和对象以及数组都存储在堆上,因为堆可能不是连续的,因此数组也可能不是连续的。

4)在python中,如果使用scipy

,可以创建一个连续的数组

答案 3 :(得分:0)

我不能详细谈论Java,尽管我的理解是给出以下代码

int arr[] = new int[N];

本地(堆栈)变量arr包含对堆上数组对象的引用,为我们提供如下布局:

          +---+
     arr: |   |---+
          +---+   |
           ...    |
          +---+   |
      cp: |   |<--+  class pointer 
          +---+ 
     flg: |   |      flags
          +---+
     lck: |   |      locks
          +---+
      sz: |   |      size
          +---+
  arr[0]: |   |
          +---+
  arr[1]: |   |
          +---+
           ...
          +---+
arr[N-1]: |   |
          +---+

对于基本类型数组,值直接存储在arr[0]arr[1]等中。对于类类型数组,数组的每个元素都存储对该实例的引用类,所以有另一层间接。引用本身是连续存储的,但它们指向的实例不是(或至少不保证)。

C和C ++数组要复杂得多。给出以下代码:

 int arr[N];

你得到以下信息:

          +---+
  arr[0]: |   |
          +---+ 
  arr[1]: |   |
          +---+ 
           ...
          +---+
arr[N-1]: |   |
          +---+

C阵列没有间接或元数据。没有为对象arr留出存储空间来指向数组的第一个元素。如果数组具有auto范围(意味着它在块中声明而不是static),则数组元素的内存分配与任何局部变量相同。

对于任何类型TT arr[N]都会预留N个连续元素来存储T类型的值。如果T是令人讨厌的struct类型,那么T a[N]会存储该N类型的struct个连续实例。