พื้นที่หน่วยความจำถูกใช้โดยวัตถุหนึ่งตัวที่มี 100 คุณลักษณะเหมือนกับของวัตถุ 100 ชิ้นโดยที่มีหนึ่งแอตทริบิวต์
มีการจัดสรรหน่วยความจำเท่าใดสำหรับวัตถุ?
มีการใช้พื้นที่เพิ่มเติมเท่าใดเมื่อเพิ่มแอตทริบิวต์
พื้นที่หน่วยความจำถูกใช้โดยวัตถุหนึ่งตัวที่มี 100 คุณลักษณะเหมือนกับของวัตถุ 100 ชิ้นโดยที่มีหนึ่งแอตทริบิวต์
มีการจัดสรรหน่วยความจำเท่าใดสำหรับวัตถุ?
มีการใช้พื้นที่เพิ่มเติมเท่าใดเมื่อเพิ่มแอตทริบิวต์
คำตอบ:
Mindprodชี้ให้เห็นว่านี่ไม่ใช่คำถามที่ตรงไปตรงมาที่จะตอบ:
JVM มีอิสระในการจัดเก็บข้อมูลไม่ว่าจะเป็นที่ชื่นชอบภายในองค์กรไม่ว่าใหญ่หรือเล็กก็ตามด้วยการแพ็ดดิ้งหรือโอเวอร์เฮดในปริมาณที่พอเหมาะ
ยกตัวอย่างเช่น JVM หรือคอมไพเลอร์พื้นเมืองอาจตัดสินใจที่จะเก็บboolean[]
ใน 64BitSet
บิตชิ้นยาวเช่น มันไม่จำเป็นต้องบอกคุณตราบใดที่โปรแกรมให้คำตอบเดียวกัน
- มันอาจจัดสรรวัตถุชั่วคราวบางอย่างในกองซ้อน
- มันอาจปรับค่าตัวแปรหรือวิธีการบางอย่างให้เหมาะสมที่สุดโดยแทนที่ค่าคงที่
- มันอาจจะเป็นวิธีการหรือลูปเช่นการรวบรวมวิธีการสองเวอร์ชันแต่ละวิธีได้รับการปรับให้เหมาะสมสำหรับสถานการณ์ที่แน่นอน
แน่นอนว่าฮาร์ดแวร์และระบบปฏิบัติการมีหลายแคชบนชิปแคชแคช SRAM แคช DRAM ชุดการทำงาน RAM ทั่วไปและที่เก็บข้อมูลสำรองบนดิสก์ ข้อมูลของคุณอาจซ้ำซ้อนในทุกระดับแคช ความซับซ้อนทั้งหมดนี้หมายความว่าคุณสามารถคาดการณ์ปริมาณการใช้ RAM ได้อย่างคร่าวๆ
คุณสามารถใช้Instrumentation.getObjectSize()
เพื่อรับการประเมินของหน่วยความจำที่ใช้โดยวัตถุ
เห็นภาพที่เกิดขึ้นจริงรูปแบบวัตถุรอยและการอ้างอิง, คุณสามารถใช้JOL (Java วัตถุเค้าโครง) เครื่องมือ
ใน JDK 64- บิตที่ทันสมัยวัตถุมีส่วนหัว 12 ไบต์เพิ่มเป็น 8 ไบต์ดังนั้นขนาดวัตถุขั้นต่ำคือ 16 ไบต์ สำหรับ JVM แบบ 32 บิตค่าใช้จ่ายจะอยู่ที่ 8 ไบต์ซึ่งจะเพิ่มเป็น 4 ไบต์ (จากคำตอบของ Dmitry Spikhalskiy , คำตอบ Jayen ของและJavaWorld .)
โดยปกติแล้วการอ้างอิง 4 ไบต์บนแพลตฟอร์ม 32bit หรือ 64bit แพลตฟอร์มถึง-Xmx32G
; และ 8 ไบต์เหนือ 32Gb ( -Xmx32G
) (ดูการอ้างอิงวัตถุที่บีบอัด )
เป็นผลให้ JVM แบบ 64 บิตโดยทั่วไปจะต้องการพื้นที่ฮีปเพิ่มขึ้น 30-50% ( ฉันควรใช้ JVM 32- หรือ 64- บิต , 2012, JDK 1.7)
wrapper ชนิดบรรจุกล่องมีค่าใช้จ่ายเมื่อเทียบกับประเภทดั้งเดิม (จากJavaWorld ):
Integer
: ผลลัพธ์ขนาด 16 ไบต์แย่กว่าที่ฉันคาดไว้เล็กน้อยเพราะint
ค่าสามารถเพิ่มเป็น 4 ไบต์พิเศษได้ ใช้Integer
ค่าใช้จ่ายฉันเป็นค่าใช้จ่ายหน่วยความจำร้อยละ 300 เมื่อเทียบกับเมื่อฉันสามารถเก็บค่าเป็นชนิดดั้งเดิม
Long
: 16 ไบต์เช่นกัน: ชัดเจนขนาดวัตถุจริงบนฮีปนั้นขึ้นอยู่กับการจัดตำแหน่งหน่วยความจำระดับต่ำโดยการใช้ JVM เฉพาะสำหรับประเภท CPU เฉพาะ ดูเหมือนว่าLong
จะเป็น 8 ไบต์ของค่าใช้จ่ายของวัตถุและเพิ่มอีก 8 ไบต์สำหรับค่าความยาวจริง ในทางตรงกันข้ามInteger
มีรูขนาด 4 ไบต์ที่ไม่ได้ใช้งานมากที่สุดเนื่องจาก JVM ฉันใช้การจัดเรียงวัตถุบนขอบเขตของคำ 8 ไบต์
ภาชนะอื่น ๆ มีราคาแพงเกินไป:
อาร์เรย์หลายมิติ : มันมีความประหลาดใจอีกอย่างหนึ่ง
นักพัฒนามักใช้โครงสร้างเช่นint[dim1][dim2]
ในการคำนวณเชิงตัวเลขและเชิงวิทยาศาสตร์ใน
int[dim1][dim2]
อินสแตนซ์อาร์เรย์ทุกint[dim2]
อาร์เรย์ที่ซ้อนกันจะอยู่Object
ในสิทธิ์ของตนเอง แต่ละอันจะเพิ่มโอเวอร์เฮดของอาร์เรย์ขนาด 16 ไบต์ตามปกติ เมื่อฉันไม่ต้องการอาเรย์รูปสามเหลี่ยมหรือ ragged นั่นหมายถึงค่าใช้จ่ายที่บริสุทธิ์ ผลกระทบจะเพิ่มขึ้นเมื่อขนาดของอาร์เรย์แตกต่างกันอย่างมากตัวอย่างเช่น
int[128][2]
อินสแตนซ์ใช้เวลา 3,600 ไบต์ เมื่อเปรียบเทียบกับ 1,040 ไบต์ที่int[256]
อินสแตนซ์ใช้ (ซึ่งมีความจุเท่ากัน) 3,600 ไบต์แทนค่าใช้จ่าย 246 เปอร์เซ็นต์ ในกรณีที่รุนแรงbyte[256][1]
ปัจจัยค่าใช้จ่ายเกือบ 19! เปรียบเทียบกับสถานการณ์ C / C ++ ที่ไวยากรณ์เดียวกันไม่ได้เพิ่มค่าใช้จ่ายในการจัดเก็บใด ๆ
String
: การString
เติบโตของหน่วยความจำติดตามการเติบโตของอาร์เรย์ภายใน อย่างไรก็ตามString
คลาสเพิ่มค่าใช้จ่ายอีก 24 ไบต์สำหรับ
String
ขนาดที่ไม่มีอักขระ 10 ตัวหรือน้อยกว่านั้นค่าใช้จ่ายที่เพิ่มจะสัมพันธ์กับเพย์โหลดที่มีประโยชน์ (2 ไบต์สำหรับถ่านแต่ละตัวบวก 4 ไบต์สำหรับความยาว) ตั้งแต่ 100 ถึง 400 เปอร์เซ็นต์
พิจารณาวัตถุตัวอย่างนี้:
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
ผลรวม na wouldve จะแนะนำว่าอินสแตนซ์ของX
จะใช้ 17 ไบต์ อย่างไรก็ตามเนื่องจากการจัดตำแหน่ง (หรือที่เรียกว่าการขยาย) JVM จะจัดสรรหน่วยความจำเป็นทวีคูณ 8 ไบต์ดังนั้นแทนที่จะเป็น 17 ไบต์จึงจะจัดสรร 24 ไบต์
มันขึ้นอยู่กับสถาปัตยกรรม / jdk สำหรับสถาปัตยกรรม JDK และ 64 บิตที่ทันสมัยวัตถุมีส่วนหัว 12 ไบต์และแพ็ดดิง 8 ไบต์ดังนั้นขนาดวัตถุขั้นต่ำคือ 16 ไบต์ คุณสามารถใช้เครื่องมือที่เรียกว่าJava Object Layoutเพื่อกำหนดขนาดและรับรายละเอียดเกี่ยวกับเค้าโครงของวัตถุและโครงสร้างภายในของเอนทิตีใด ๆ หรือเดาข้อมูลนี้โดยอ้างอิงคลาส ตัวอย่างของเอาต์พุตสำหรับ Integer บนสภาพแวดล้อมของฉัน:
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
ดังนั้นสำหรับ Integer ขนาดอินสแตนซ์คือ 16 ไบต์เนื่องจาก int 4 ไบต์บีบอัดในสถานที่ทันทีหลังจากส่วนหัวและก่อนที่จะขยายขอบเขต
ตัวอย่างโค้ด:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
หากคุณใช้ Maven เพื่อรับ JOL:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
แต่ละวัตถุมีค่าใช้จ่ายที่แน่นอนสำหรับการตรวจสอบที่เกี่ยวข้องและข้อมูลประเภทเช่นเดียวกับเขตข้อมูลตัวเอง นอกเหนือจากนั้นฟิลด์สามารถจัดวางได้ค่อนข้างมาก แต่ JVM เห็นว่าเหมาะสม (ฉันเชื่อ) - แต่ดังที่แสดงไว้ในคำตอบอื่นอย่างน้อยJVM บางตัวจะอัดแน่นพอสมควร พิจารณาชั้นเรียนเช่นนี้
public class SingleByte
{
private byte b;
}
VS
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
บน JVM แบบ 32 บิตฉันคาดว่าจะมีอินสแตนซ์ 100 รายการที่SingleByte
จะใช้ 1200 ไบต์ (8 ไบต์ของค่าใช้จ่าย + 4 ไบต์สำหรับเขตข้อมูลเนื่องจากการขยาย / การจัดตำแหน่ง) ฉันคาดว่าหนึ่งอินสแตนซ์ของOneHundredBytes
การใช้ 108 ไบต์ - ค่าใช้จ่ายแล้ว 100 ไบต์บรรจุ แน่นอนว่ามันอาจแตกต่างกันไปตาม JVM - การใช้งานอย่างหนึ่งอาจตัดสินใจที่จะไม่แพ็คฟิลด์ในOneHundredBytes
ซึ่งนำไปสู่การใช้ 408 ไบต์ (= 8 ไบต์ค่าใช้จ่าย + 4 * 100 ไบต์จัดแนว / padded) ใน JVM 64 บิตค่าใช้จ่ายอาจสูงกว่าด้วย (ไม่แน่ใจ)
แก้ไข: ดูความคิดเห็นด้านล่าง; เห็นได้ชัดว่า HotSpot มีขอบเขตถึง 8 ไบต์แทนที่จะเป็น 32 ดังนั้นแต่ละอินสแตนซ์SingleByte
จะใช้เวลา 16 ไบต์
ไม่ว่าจะด้วยวิธีใดก็ตาม "วัตถุขนาดใหญ่เดียว" อย่างน้อยก็มีประสิทธิภาพเท่ากับวัตถุขนาดเล็กหลายชิ้น - สำหรับกรณีง่าย ๆ เช่นนี้
จำนวนหน่วยความจำที่ใช้ / ฟรีทั้งหมดของโปรแกรมสามารถรับได้ในโปรแกรมผ่าน
java.lang.Runtime.getRuntime();
รันไทม์มีหลายวิธีที่เกี่ยวข้องกับหน่วยความจำ ตัวอย่างการเข้ารหัสต่อไปนี้แสดงให้เห็นถึงการใช้งาน
package test;
import java.util.ArrayList;
import java.util.List;
public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
// I assume you will know how to create a object Person yourself...
List < Person > list = new ArrayList < Person > ();
for (int i = 0; i <= 100000; i++) {
list.add(new Person("Jim", "Knopf"));
}
// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
ปรากฏว่าวัตถุทุกชิ้นมีค่าใช้จ่าย 16 ไบต์บนระบบ 32 บิต (และ 24- ไบต์บนระบบ 64 บิต)
http://algs4.cs.princeton.edu/14analysis/เป็นแหล่งข้อมูลที่ดี ตัวอย่างหนึ่งของคนดีหลายคนดังต่อไปนี้
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdfนอกจากนี้ยังมีข้อมูลมากเช่น:
พื้นที่หน่วยความจำถูกใช้โดยวัตถุหนึ่งตัวที่มี 100 คุณลักษณะเหมือนกับของวัตถุ 100 ชิ้นโดยที่มีหนึ่งแอตทริบิวต์
เลขที่
มีการจัดสรรหน่วยความจำเท่าใดสำหรับวัตถุ?
มีการใช้พื้นที่เพิ่มเติมเท่าใดเมื่อเพิ่มแอตทริบิวต์
ไม่การลงทะเบียนวัตถุจะใช้หน่วยความจำเล็กน้อย วัตถุ 100 รายการที่มีแอตทริบิวต์ 1 รายการจะใช้หน่วยความจำเพิ่มขึ้น
คำถามจะเป็นคำถามที่กว้างมาก
มันขึ้นอยู่กับตัวแปรคลาสหรือคุณอาจเรียกว่าการใช้หน่วยความจำสถานะในจาวา
นอกจากนี้ยังมีข้อกำหนดหน่วยความจำเพิ่มเติมบางส่วนสำหรับส่วนหัวและการอ้างอิง
หน่วยความจำฮีปที่ใช้โดยวัตถุ Java ประกอบด้วย
หน่วยความจำสำหรับฟิลด์ดั้งเดิมตามขนาด (ดูด้านล่างสำหรับขนาดของประเภทดั้งเดิม);
หน่วยความจำสำหรับเขตข้อมูลอ้างอิง (4 ไบต์แต่ละรายการ);
ส่วนหัวของวัตถุซึ่งประกอบด้วยข้อมูล "แม่บ้าน" สองสามไบต์
วัตถุใน java ยังต้องการข้อมูล "การดูแล" บางอย่างเช่นการบันทึกคลาสของ ID และสถานะของวัตถุเช่นว่าวัตถุนั้นสามารถเข้าถึงได้ในปัจจุบันล็อคซิงโครไนซ์เป็นต้นหรือไม่
ขนาดส่วนหัวของวัตถุ Java แตกต่างกันไปใน 32 และ 64 บิต jvm
แม้ว่าสิ่งเหล่านี้จะเป็นหน่วยความจำหลักที่ผู้บริโภค jvm ต้องใช้นอกจากนี้บางฟิลด์ต้องการสำหรับการจัดวางโค้ด ฯลฯ
ขนาดของประเภทดั้งเดิม
บูลีนและไบต์ - 1
อักขระสั้น & - 2
int & float - 4
ยาว & สองเท่า - 8
ฉันได้ผลลัพธ์ที่ดีมากจากวิธีjava.lang.instrument.Instrumentation ที่กล่าวถึงในคำตอบอื่น สำหรับตัวอย่างที่ดีของการใช้งานดูรายการInstrumentation Memory Counterจาก JavaSpecialists 'Newsletter และไลบรารีjava.sizeOfบน SourceForge
ในกรณีที่มันเป็นประโยชน์กับทุกคนที่คุณสามารถดาวน์โหลดได้จากเว็บไซต์ของฉันมีขนาดเล็กตัวแทน Java สำหรับการสอบถามการใช้งานหน่วยความจำของวัตถุ มันจะช่วยให้คุณค้นหาการใช้หน่วยความจำ "ลึก" เช่นกัน
(String, Integer)
แคชของ Guava ใช้ต่อหน่วยองค์ประกอบเท่าใด ขอบคุณ!
ไม่วัตถุขนาดเล็ก 100 ชิ้นต้องการข้อมูลเพิ่มเติม (หน่วยความจำ) มากกว่าวัตถุขนาดใหญ่หนึ่งชิ้น
กฎเกี่ยวกับปริมาณการใช้หน่วยความจำขึ้นอยู่กับการนำ JVM มาใช้และสถาปัตยกรรม CPU (ตัวอย่างเช่น 32 บิตต่อ 64 บิต)
สำหรับรายละเอียดกฎสำหรับ SUN JVM ตรวจสอบบล็อกเก่าของฉัน
ขอแสดงความนับถือ มาร์คัส