Một mảng Java nguyên thủy có được lưu trữ trong ngăn xếp hay đống không?


85

Tôi có một khai báo mảng như sau:

int a[];

Đây alà một mảng kiểu nguyên thủy int. Mảng này được lưu trữ ở đâu? Nó được lưu trữ trên heap hay stack? Đây là một kiểu primitve int, tất cả các kiểu nguyên thủy không được lưu trữ trên heap.


38
Đó không phải là một mảng. Nó là một tham chiếu đến một mảng. Bản thân tham chiếu có thể được lưu trữ trên heap nếu nó là thành viên của một lớp hoặc đối tượng hoặc trên ngăn xếp nếu nó là một biến cục bộ trong một phương thức. Và các kiểu nguyên thủy có thể được lưu trữ trên heap nếu chúng là thành viên của một lớp hoặc đối tượng.
UncleO

Câu trả lời:


143

Như gurukulki đã nói, nó được lưu trữ trên đống. Tuy nhiên, bài đăng của bạn gợi ý một sự hiểu lầm có thể là do một số người có thiện chí tuyên truyền huyền thoại rằng "những người nguyên thủy luôn sống trên chồng". Điều này là không đúng sự thật. Biến cục bộ có giá trị của chúng trên ngăn xếp, nhưng không phải tất cả các biến nguyên thủy đều là cục bộ ...

Ví dụ, hãy xem xét điều này:

public class Foo
{
    int value;
}
...

public void someOtherMethod()
{
    Foo f = new Foo();
    ...
}

Bây giờ, f.valuesống ở đâu? Huyền thoại cho rằng nó nằm trên ngăn xếp - nhưng thực ra nó là một phần của Foođối tượng mới và sống trên đống 1 . (Lưu ý rằng giá trị của fchính nó là một tham chiếu và nằm trên ngăn xếp.)

Từ đó, đây là một bước dễ dàng để tạo mảng. Bạn có thể nghĩ về một mảng chỉ là rất nhiều biến - vì vậy new int[3]cũng giống như việc có một lớp có dạng này:

public class ArrayInt3
{
    public readonly int length = 3;
    public int value0;
    public int value1;
    public int value2;
}

1 Trong thực tế, nó phức tạp hơn thế này. Sự phân biệt ngăn xếp / đống hầu hết là một chi tiết triển khai - tôi tin rằng một số JVM, có thể là những JVM thử nghiệm, có thể biết khi nào một đối tượng không bao giờ "thoát" khỏi một phương thức và có thể phân bổ toàn bộ đối tượng trên ngăn xếp. Tuy nhiên, về mặt khái niệm , nó nằm trên đống, nếu bạn chọn quan tâm.


1
Giới thiệu về "Escape analyisis" trong Java: blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead Nó nói rằng nó có mặt kể từ khi bản phát hành truy cập sớm JDK 6 Update 14 và được bật theo mặc định kể từ Bản cập nhật JDK 6 23.
Guido:

nó có thay đổi bất cứ điều gì nếu mảng là công khai tĩnh cuối cùng? Vậy nó không phải là một phần của nhóm hằng số sao?
Malachiasz

@Malachiasz: Không. Một mảng không bao giờ là một hằng số.
Jon Skeet

@JonSkeet: Trong tất cả các phiên bản của thư viện Java mà tôi biết, mọi phiên bản đều Stringđược hỗ trợ bởi a char[]. Tôi tin rằng các chuỗi ký tự được lưu trữ trong nhóm hằng số công khai; đối với tối ưu hóa GC, điều đó có nghĩa là các mảng hỗ trợ cũng phải được lưu trữ tương tự (nếu không thì nhóm hằng số sẽ phải được quét trong bất kỳ chu kỳ GC nào trong đó mảng hỗ trợ sẽ đủ điều kiện để thu thập).
supercat

@supercat: Vâng, điều đó có lý. Nhưng bất kỳ mảng nào bạn tự khai báo sẽ không bao giờ trở thành một phần của nhóm hằng số.
Jon Skeet

37

Nó sẽ được lưu trữ trên heap

vì mảng là một đối tượng trong java.

CHỈNH SỬA : nếu bạn có

int [] testScores; 
testScores = new int[4];

Hãy coi đoạn mã này giống như nói với trình biên dịch, "Tạo một đối tượng mảng sẽ chứa bốn giá trị nguyên và gán nó cho biến tham chiếu có tên testScores. Ngoài ra, hãy tiếp tục và đặt từng intphần tử thành 0. Cảm ơn."


2
Và biến tham chiếu có tên testScores (trỏ đến mảng trên heap) sẽ nằm trên ngăn xếp.
Zaki

11
Mã chỉ nói "Cảm ơn" nếu bạn cung cấp -gtùy chọn cho trình biên dịch. Nếu không nó sẽ được tối ưu hóa đi.
mob

1
@mob Bạn đang giả định rằng đây là mã duy nhất. Có lẽ tốt hơn nên cho rằng hai dòng này là một phần của chương trình lớn hơn thực sự sử dụng mảng.
Code-Apprentice,

22

Nó là một mảng các kiểu nguyên thủy mà bản thân nó không phải là kiểu nguyên thủy. Một nguyên tắc nhỏ là khi từ khóa mới được tham gia, kết quả sẽ nằm trên đống.


18

Tôi chỉ muốn chia sẻ một vài bài kiểm tra tôi đã chạy về chủ đề này.

Mảng cỡ 10 triệu

public static void main(String[] args) {
    memInfo();
    double a[] = new double[10000000];
    memInfo();
}

Đầu ra:

------------------------
max mem = 130.0 MB
total mem = 85.0 MB
free mem = 83.6 MB
used mem = 1.4 MB
------------------------
------------------------
max mem = 130.0 MB
total mem = 130.0 MB
free mem = 48.9 MB
used mem = 81.1 MB
------------------------

Như bạn thấy, kích thước heap đã sử dụng được tăng lên ~ 80 MB, là 10m * sizeof (gấp đôi).

Nhưng nếu chúng ta sử dụng Double thay vì double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    memInfo();
}

Đầu ra sẽ hiển thị 40MB. Chúng tôi chỉ có tham chiếu Đôi, chúng không được khởi tạo.

Làm đầy nó với Double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];      
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq;
    }
    memInfo();
}

Vẫn là 40MB. Vì chúng đều trỏ đến cùng một đối tượng Double.

Khởi tạo với double thay thế

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq.doubleValue();
    }
    memInfo();
}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Hàng

a[i] = qq.doubleValue();

tương đương với

a[i] = Double.valueOf(qq.doubleValue());

tương đương với

a[i] = new Double(qq.doubleValue());

Vì chúng tôi tạo các đối tượng Đôi mới mỗi lần, chúng tôi sẽ thổi bay đống. Điều này cho thấy các giá trị bên trong lớp Double được lưu trữ trong heap.


1
Bạn có thể dán chi tiết mã của memInfo () được không? :)
hedleyyan

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.