เหตุใดจึงไม่กำหนดขนาดดั้งเดิมของบูลีนของ Java


111

ข้อมูลจำเพาะ Java เครื่องเสมือนบอกว่ามีการสนับสนุนที่ จำกัด สำหรับบูลดั้งเดิมประเภท

ไม่มีคำแนะนำของเครื่องเสมือน Java เฉพาะสำหรับการดำเนินการกับค่าบูลีนเท่านั้น แต่นิพจน์ในภาษาโปรแกรม Java ที่ทำงานกับค่าบูลีนจะถูกคอมไพล์เพื่อใช้ค่าของชนิดข้อมูล Java virtual machine int

ข้างต้นบอกเป็นนัยว่า (แม้ว่าฉันอาจตีความผิด) ว่าประเภทข้อมูล int ถูกใช้เมื่อทำงานกับบูลีน แต่นี่เป็นการสร้างหน่วยความจำ 32 บิต เนื่องจากบูลีนแสดงถึงข้อมูล 1 บิตเท่านั้น:

  • เหตุใดจึงไม่ใช้ไบต์หรือชนิดสั้นเป็นพร็อกซีสำหรับบูลีนแทนที่จะเป็น int
  • สำหรับ JVM ใด ๆ วิธีใดที่น่าเชื่อถือที่สุดในการค้นหาว่าหน่วยความจำที่ใช้จัดเก็บประเภทบูลีนคือเท่าใด

คำตอบ:


116

คำตอบสั้น ๆ : ใช่ค่าบูลีนถูกจัดการเป็นเอนทิตี 32 บิต แต่อาร์เรย์ของบูลีนใช้ 1 ไบต์ต่อองค์ประกอบ

คำตอบที่ยาวขึ้น: JVM ใช้สแต็กเซลล์ 32 บิตใช้เพื่อเก็บตัวแปรโลคัลอาร์กิวเมนต์เมธอดและค่านิพจน์ สารดึกดำบรรพ์ที่มีขนาดเล็กกว่า 1 เซลล์จะถูกนำออกมาส่วนดั้งเดิมที่มีขนาดใหญ่กว่า 32 บิต (ยาวและสองเท่า) ใช้เวลา 2 เซลล์ เทคนิคนี้ช่วยลดจำนวน opcodes ให้เหลือน้อยที่สุด แต่มีผลข้างเคียงที่แปลกประหลาด (เช่นความจำเป็นในการปิดบังไบต์)

Primitives ที่เก็บในอาร์เรย์อาจใช้น้อยกว่า 32 บิตและมี opcodes ที่แตกต่างกันในการโหลดและจัดเก็บค่าดั้งเดิมจากอาร์เรย์ ค่าบูลีนและไบต์ทั้งคู่ใช้baloadและbastoreopcodes ซึ่งหมายความว่าอาร์เรย์บูลีนใช้เวลา 1 ไบต์ต่อองค์ประกอบ

เท่าที่โครงร่างออบเจ็กต์ในหน่วยความจำดำเนินไปสิ่งนี้จะครอบคลุมภายใต้กฎ "การนำไปใช้งานส่วนตัว" ซึ่งอาจเป็น 1 บิต 1 ไบต์หรือตามที่ผู้โพสต์อื่นระบุไว้ซึ่งสอดคล้องกับขอบเขตคำสองคำ 64 บิต ส่วนใหญ่จะใช้ขนาดคำพื้นฐานของฮาร์ดแวร์พื้นฐาน (32 หรือ 64 บิต)


เท่าที่จะลดจำนวนพื้นที่ที่บูลีนใช้: มันไม่ใช่ปัญหาสำหรับแอปพลิเคชันส่วนใหญ่ เฟรมสแต็ก (ถือตัวแปรโลคัลและอาร์กิวเมนต์เมธอด) ไม่ใหญ่มากนักและในโครงร่างใหญ่บูลีนแยกในออบเจ็กต์ก็ไม่ได้ใหญ่ขนาดนั้นเช่นกัน หากคุณมีวัตถุจำนวนมากที่มีบูลีนจำนวนมากคุณสามารถใช้บิตฟิลด์ที่จัดการผ่านทาง getters และ setters ของคุณ อย่างไรก็ตามคุณจะต้องเสียค่าปรับในเวลา CPU ซึ่งอาจมากกว่าค่าปรับในหน่วยความจำ


สำหรับสมาชิกคลาสบูลีน / ไบต์มันเป็นความจริงหรือไม่ที่พวกมันเป็น 4 ไบต์ด้วย อินสแตนซ์คลาสได้รับการจัดสรรโดยรวมบนสแต็กดังนั้นฉันจึงนึกได้ว่า JVM น่าจะใช้ 1 ไบต์ต่อสมาชิกบูลีน / ไบต์และสุดท้ายทำการจัดตำแหน่ง 4 ไบต์สำหรับอินสแตนซ์คลาสที่สมบูรณ์ เป็นอย่างนั้นหรือ (หากคุณมีข้อมูลอ้างอิงที่พิสูจน์ได้โปรดแบ่งปัน)
dma_k

@dma_k: ตามที่ระบุไว้ในคำตอบของฉันเลย์เอาต์ของอินสแตนซ์คลาสขึ้นอยู่กับการใช้งาน อย่างไรก็ตามโปรดทราบว่าอินสแตนซ์คลาสไม่ได้ถูกเก็บไว้ในสแต็ก แต่จะถูกเก็บไว้ในฮีป (แม้ว่าคุณจะเห็นการอ้างอิงถึง JDK 7 "การวิเคราะห์การหลบหนี" ที่กำลังเคลื่อนย้ายวัตถุจากสแต็กไปยังฮีป แต่ดูเหมือนจะไม่เป็นเช่นนั้น ดูjava.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory

1
บางครั้งการบรรจุบูลีนอาจเร็วกว่าก็จริง เมื่อใดก็ตามที่ขนาดแคชมีความสำคัญการบรรจุสิ่งต่างๆอาจดีกว่า ตัวอย่างเช่นตะแกรงไพรม์แบบแบ่งส่วนจะทำงานเป็นชิ้นขนาด 32 kB (ขนาดแคช L1) เร็วกว่าตะแกรงที่ไม่แบ่งส่วน มีค่าใช้จ่ายบางส่วนระหว่างชิ้นและบรรจุภัณฑ์ที่คุณจ่ายค่าโสหุ้ยน้อยกว่าแปดเท่า ฉันยังไม่ได้วัดเลย
maaartinus

7

บูลีนเดี่ยวที่ใดที่หนึ่งในลำดับชั้นการสืบทอดสามารถใช้ได้ถึง 8 ไบต์! สาเหตุนี้เกิดจากช่องว่างภายใน สามารถดูรายละเอียดเพิ่มเติมได้ในวัตถุ Java ของฉันใช้หน่วยความจำเท่าใด :

กลับมาที่คำถามที่ว่าบูลีนใช้ไปเท่าไรใช่มันกินอย่างน้อยหนึ่งไบต์ แต่เนื่องจากกฎการจัดตำแหน่งอาจใช้มากกว่านี้ IMHO น่าสนใจกว่าที่ทราบว่าบูลีน [] จะใช้หนึ่งไบต์ต่อรายการไม่ใช่หนึ่งบิตบวกค่าใช้จ่ายบางส่วนเนื่องจากการจัดตำแหน่งและสำหรับฟิลด์ขนาดของอาร์เรย์ มีอัลกอริธึมกราฟที่ช่องบิตขนาดใหญ่มีประโยชน์และคุณต้องระวังว่าถ้าคุณใช้บูลีน [] คุณต้องใช้หน่วยความจำมากกว่าที่จำเป็นจริงๆเกือบ 8 เท่า (1 ไบต์กับ 1 บิต)


จะใช้บูลีน [] อย่างไรต่อไป?
Thomas Jung

บูลีน [] สามารถใช้สำหรับมาสก์ได้ บางครั้ง BitSet อาจดีกว่าเพราะมีวิธีการที่มีประโยชน์บางอย่าง
Michael Munsey

5

Java in a Nutshellรุ่นที่ 5 (O'Reilly) กล่าวว่าประเภทดั้งเดิมของบูลีนคือ 1 ไบต์ ซึ่งอาจผิดพลาดขึ้นอยู่กับสิ่งที่การตรวจสอบฮีปแสดง ฉันสงสัยว่า JVM ส่วนใหญ่มีปัญหาในการจัดสรรตัวแปรน้อยกว่าหนึ่งไบต์หรือไม่


3

การแมปบูลีนทำได้โดยคำนึงถึง CPU 32 บิต ค่า int มี 32 บิตเพื่อให้สามารถประมวลผลได้ในการดำเนินการเดียว

นี่เป็นวิธีแก้ปัญหาจากJava IAQ ของ Peter Norvig: คำถามที่มีคำตอบไม่บ่อยนักเพื่อวัดขนาด (ด้วยความไม่แม่นยำบางอย่าง):

static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end =  runtime.freememory();
System.out.println("That took " + (start-end) + " bytes.");

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

2

ซีพียูทำงานตามความยาวประเภทข้อมูลเฉพาะ ในกรณีของซีพียู 32 บิตจะมีความยาว 32 บิตดังนั้นสิ่งที่คุณเรียกว่า 'int' ใน Java ทุกสิ่งที่อยู่ด้านล่างหรือด้านบนที่ต้องเติมหรือแบ่งให้ยาวเท่านี้ก่อนที่ CPU จะประมวลผลได้ ใช้เวลาไม่มาก แต่ถ้าคุณต้องการ CPU 2 รอบแทนที่จะเป็น 1 สำหรับการใช้งานพื้นฐานนั่นหมายถึงค่าใช้จ่าย / เวลาที่เพิ่มขึ้นเป็นสองเท่า

ข้อมูลจำเพาะนี้มีไว้สำหรับ CPU 32 บิตเพื่อให้สามารถประมวลผลบูลีนด้วยประเภทข้อมูลดั้งเดิมได้

คุณสามารถมีได้ที่นี่เท่านั้น: ความเร็วหรือหน่วยความจำ - SUN ตัดสินใจเรื่องความเร็ว


1

บูลีนแสดงถึงข้อมูลเพียงเล็กน้อย แต่ "ขนาด" ไม่ใช่สิ่งที่กำหนดไว้อย่างแม่นยำบทแนะนำ Sun Java กล่าว ลิเทอรัลบูลีนมีค่าที่เป็นไปได้เพียงสองค่าเท่านั้นคือจริงและเท็จ ดูประเภทข้อมูล Javaสำหรับรายละเอียด


-10

ทำไมไม่สร้างไฟล์. java แบบนี้:

Empty.java

class Empty{
}

และชั้นเดียวเช่นนี้:

NotEmpty.java

class NotEmpty{
   boolean b;
}

รวบรวมทั้งสองไฟล์และเปรียบเทียบไฟล์. class ด้วยโปรแกรมแก้ไขฐานสิบหก


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