อาร์เรย์ดั้งเดิมของ Java ถูกเก็บไว้ในสแต็กหรือฮีปหรือไม่?


85

ฉันมีการประกาศอาร์เรย์ดังนี้:

int a[];

นี่aคืออาร์เรย์ของintประเภทดั้งเดิม อาร์เรย์นี้เก็บไว้ที่ไหน? เก็บไว้ในฮีปหรือสแต็ก? นี่คือประเภทไพรม์ไทม์ชนิดintดั้งเดิมทั้งหมดจะไม่ถูกเก็บไว้ในฮีป


39
นั่นไม่ใช่อาร์เรย์ เป็นการอ้างอิงถึงอาร์เรย์ การอ้างอิงนั้นสามารถเก็บไว้ในฮีปได้หากเป็นสมาชิกของคลาสหรืออ็อบเจ็กต์หรือบนสแตกหากเป็นตัวแปรโลคัลในเมธอด และชนิดดั้งเดิมสามารถเก็บไว้ในฮีปได้หากเป็นสมาชิกของคลาสหรืออ็อบเจ็กต์
uncleO

คำตอบ:


145

ตามที่ gurukulki กล่าวไว้มันถูกเก็บไว้ในกอง อย่างไรก็ตามโพสต์ของคุณแสดงให้เห็นถึงความเข้าใจผิดซึ่งอาจเกิดจากบุคคลที่มีเจตนาดีบางคนเผยแพร่ตำนานที่ว่า นี่ไม่เป็นความจริง ตัวแปรท้องถิ่นมีค่าในสแต็ก แต่ไม่ใช่ตัวแปรดั้งเดิมทั้งหมดที่เป็นโลคัล ...

ตัวอย่างเช่นพิจารณาสิ่งนี้:

public class Foo
{
    int value;
}
...

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

ตอนนี้f.valueอาศัยอยู่ที่ไหน? ตำนานขอแนะนำให้มันอยู่ในสแต็ค - แต่จริง ๆ แล้วมันเป็นส่วนหนึ่งของใหม่Fooวัตถุและชีวิตในกอง1 (โปรดทราบว่าค่าของfตัวมันเองเป็นข้อมูลอ้างอิงและอยู่ในสแต็ก)

จากนั้นเป็นขั้นตอนง่ายๆในการจัดเรียงอาร์เรย์ คุณสามารถคิดว่าอาร์เรย์เป็นเพียงตัวแปรจำนวนมากดังนั้นnew int[3]ก็เหมือนกับการมีคลาสของรูปแบบนี้:

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

1อันที่จริงมันซับซ้อนกว่านี้ ความแตกต่างของสแต็ก / ฮีปส่วนใหญ่เป็นรายละเอียดการใช้งาน - ฉันเชื่อว่า JVM บางตัวอาจเป็นแบบทดลองสามารถบอกได้ว่าเมื่อใดที่วัตถุไม่เคย "หลบหนี" จากเมธอดและอาจจัดสรรวัตถุทั้งหมดบนสแต็ก อย่างไรก็ตามมันเป็นแนวความคิดเกี่ยวกับกองถ้าคุณเลือกที่จะดูแล


1
เกี่ยวกับ "Escape analyisis" ใน Java: blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead มีการระบุว่ามีอยู่ตั้งแต่การเปิดตัว JDK 6 อัปเดต 14 และ เปิดใช้งานโดยค่าเริ่มต้นตั้งแต่ JDK 6 อัปเดต 23
Guido

จะเปลี่ยนแปลงอะไรหรือไม่ถ้า array เป็น public static final? มันควรจะเป็นส่วนหนึ่งของสระคงที่ไม่ใช่หรือ?
Malachiasz

@Malachiasz: ไม่. อาร์เรย์ไม่เคยเป็นค่าคงที่
Jon Skeet

@JonSkeet: ในไลบรารี Java ทุกเวอร์ชันที่ฉันทราบทุกเวอร์ชันStringได้รับการสนับสนุนโดยไฟล์char[]. ฉันเชื่อว่าสตริงตามตัวอักษรถูกเก็บไว้ในพูลคงที่สาธารณะ สำหรับการเพิ่มประสิทธิภาพ GC นั่นหมายความว่าควรจัดเก็บอาร์เรย์สำรองในลักษณะเดียวกัน (มิฉะนั้นจะต้องสแกนพูลคงที่ในระหว่างรอบ GC ใด ๆ ที่อาร์เรย์สำรองจะมีสิทธิ์ได้รับการรวบรวม)
supercat

@supercat: ใช่มันจะสมเหตุสมผล แต่อาร์เรย์ใด ๆ ที่คุณประกาศว่าตัวเองไม่มีวันสิ้นสุดเป็นส่วนหนึ่งของพูลคงที่
Jon Skeet

37

มันจะถูกเก็บไว้ในฮีป

เนื่องจากอาร์เรย์เป็นวัตถุใน java

แก้ไข : ถ้าคุณมี

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

ให้คิดว่าโค้ดนี้พูดกับคอมไพเลอร์ว่า "สร้างออบเจ็กต์อาร์เรย์ที่จะเก็บ int สี่ตัวและกำหนดให้กับตัวแปรอ้างอิงที่ชื่อtestScoresนอกจากนี้ให้ตั้งค่าแต่ละintองค์ประกอบเป็นศูนย์ขอบคุณ"


3
และตัวแปรอ้างอิงชื่อ testScores (ชี้ไปที่อาร์เรย์บนฮีป) จะอยู่บนสแตก
Zaki

11
โค้ดจะระบุว่า "ขอบคุณ" เท่านั้นหากคุณระบุ-gตัวเลือกให้กับคอมไพเลอร์ มิฉะนั้นจะถูกเพิ่มประสิทธิภาพออกไป
ม็อบ

1
@mob คุณกำลังตั้งสมมติฐานว่านี่เป็นรหัสเดียว น่าจะดีกว่าถ้าสมมติว่าสองบรรทัดนี้เป็นส่วนหนึ่งของโปรแกรมขนาดใหญ่ที่ใช้อาร์เรย์จริงๆ
Code-Apprentice

22

มันเป็นอาร์เรย์ของประเภทดั้งเดิมซึ่งในตัวมันเองไม่ได้เป็นแบบดั้งเดิม หลักการที่ดีคือเมื่อคีย์เวิร์ดใหม่เข้ามาเกี่ยวข้องผลลัพธ์จะอยู่ในฮีป


19

ฉันแค่อยากจะแบ่งปันการทดสอบบางอย่างที่ฉันทำในหัวข้อนี้

อาร์เรย์ขนาด 10 ล้าน

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

เอาท์พุต:

------------------------
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
------------------------

ดังที่คุณเห็นขนาดฮีปที่ใช้เพิ่มขึ้น ~ 80 MB ซึ่งเป็น 10m * sizeof (double)

แต่ถ้าเราใช้ Double แทน double

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

เอาต์พุตจะแสดง 40MB เรามีเพียงการอ้างอิงสองครั้งเท่านั้นพวกเขาจะไม่เริ่มต้น

เติมด้วย 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();
}

ยังคงเป็น 40MB. เพราะทั้งหมดชี้ไปที่วัตถุคู่เดียวกัน

เริ่มต้นด้วย 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.doubleValue();
    }
    memInfo();
}

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

ไลน์

a[i] = qq.doubleValue();

เทียบเท่ากับ

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

ซึ่งเทียบเท่ากับ

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

เนื่องจากเราสร้างวัตถุคู่ใหม่ทุกครั้งเราจึงระเบิดฮีป ค่านี้แสดงค่าภายในคลาส Double จะถูกเก็บไว้ในฮีป


1
คุณช่วยวางรหัสรายละเอียดของ memInfo () ได้ไหม :)
hedleyyan

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.