1. ขีด จำกัด สูงสุดของการจัดสรรอาร์เรย์
ขนาดของอาร์เรย์ใน Java มี จำกัด เนื่องจากใช้ประเภท int เป็นตัวห้อยอาร์เรย์ ซึ่งหมายความว่าคุณไม่สามารถสมัครอาร์เรย์ที่เกินขนาดของจำนวนเต็ม max_value (2^31-1) นี่ไม่ได้หมายความว่าขีด จำกัด สูงสุดของแอปพลิเคชันหน่วยความจำของคุณคือ 2G คุณสามารถสมัครอาร์เรย์ประเภทใหญ่ ตัวอย่างเช่น:
การคัดลอกรหัสมีดังนี้:
ความยาวสุดท้าย [] ar = ใหม่ยาว [จำนวนเต็ม max_value];
สิ่งนี้จะจัดสรร 16G -8 ไบต์ เป็นเพียงกฎทั่วไปการจัดสรรเฉพาะขึ้นอยู่กับสถานการณ์จริง)
น่าเสียดายที่ใน Java เนื่องจากข้อ จำกัด ประเภทขององค์ประกอบอาร์เรย์มันจะลำบากมากขึ้นในการใช้งานหน่วยความจำ เมื่อพูดถึงอาร์เรย์ปฏิบัติการ Bytebuffer ควรเป็นคลาสที่มีประโยชน์มากที่สุดซึ่งให้วิธีการอ่านและเขียนประเภท Java ที่แตกต่างกัน ข้อเสียของมันคือประเภทอาร์เรย์เป้าหมายจะต้องเป็นไบต์ [] ซึ่งหมายความว่าแคชหน่วยความจำสูงสุดที่คุณจัดสรรอาจเป็น 2G
2. ใช้อาร์เรย์ทั้งหมดเป็นอาร์เรย์ไบต์เพื่อทำงาน
สมมติว่าหน่วยความจำ 2G นั้นอยู่ไกลจากเราในตอนนี้มันก็โอเคถ้าเป็น 16G เราได้มอบหมายให้นาน [] แต่เราต้องการใช้งานเป็นอาร์เรย์ไบต์ ใน Java เราต้องขอความช่วยเหลือจากโปรแกรมเมอร์ C - sun.misc.unsafe คลาสนี้มีสองชุดของวิธีการ: GETN (Object, Offset) วิธีนี้คือการได้รับค่าของประเภทที่ระบุจากตำแหน่งที่ Offset Offset ถูกชดเชยและส่งคืน วิธีการพัตต์ (วัตถุ, ออฟเซ็ต, ค่า) คือการเขียนค่าไปยังตำแหน่งของออฟเซ็ตของวัตถุ
น่าเสียดายที่วิธีการเหล่านี้สามารถรับหรือตั้งค่าประเภทหนึ่งเท่านั้น หากคุณคัดลอกข้อมูลจากอาร์เรย์คุณยังต้องใช้วิธีการอื่นของ Unsafe, CopyMemory (SRCOBJECT, SRCOFFSET, DESTOBJECT, DESTOFFET, COUNT) สิ่งนี้ใช้งานได้คล้ายกับ system.arraycopy แต่มันจะคัดลอกไบต์มากกว่าองค์ประกอบอาร์เรย์
ในการเข้าถึงข้อมูลของอาร์เรย์ผ่าน sun.misc.unsafe คุณต้องมีสองสิ่ง:
1. การชดเชยข้อมูลในวัตถุอาร์เรย์
2. ออฟเซ็ตขององค์ประกอบที่คัดลอกในข้อมูลอาร์เรย์
เช่นเดียวกับวัตถุ Java อื่น ๆ อาร์เรย์มีส่วนหัวของวัตถุซึ่งเก็บไว้ด้านหน้าของข้อมูลจริง ความยาวของส่วนหัวนี้สามารถรับได้โดยวิธีการที่ไม่ปลอดภัย arraybaseoffset (t []. class) โดยที่ t คือประเภทขององค์ประกอบอาร์เรย์ ขนาดขององค์ประกอบอาร์เรย์สามารถรับได้ผ่านวิธี unsafe.arrayindexscale (t []. class) ซึ่งหมายความว่าหากคุณต้องการเข้าถึงองค์ประกอบ nth ของ Type T การชดเชยออฟเซ็ตของคุณควรเป็น arrayoffset+n*arrayscale
มาเขียนตัวอย่างง่ายๆ เราจัดสรรอาร์เรย์ที่ยาวและอัปเดตไบต์ภายในไม่กี่ไบต์ เราอัปเดตองค์ประกอบสุดท้ายเป็น -1 (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) จากนั้นล้างไบต์ทั้งหมดขององค์ประกอบนี้ทีละหนึ่ง
การคัดลอกรหัสมีดังนี้:
ยาวสุดท้าย [] ar = ใหม่ยาว [1,000];
ดัชนี int สุดท้าย = ar.length - 1;
AR [ดัชนี] = -1;
System.out.println ("ก่อนเปลี่ยน =" + long.tohexstring (AR [index]));
สำหรับ (ยาว i = 0; i <8; ++ i)
-
unsafe.putByte (AR, longarrayoffset + 8l * index + i, (byte) 0);
System.out.println ("หลังจากการเปลี่ยนแปลง: i =" + i + ", val =" + long.tohexstring (AR [index]));
-
หากคุณต้องการเรียกใช้ตัวอย่างด้านบนคุณต้องเพิ่มบล็อกรหัสคงที่ต่อไปนี้ลงในคลาสทดสอบของคุณ:
การคัดลอกรหัสมีดังนี้:
ส่วนตัวคงที่ไม่ปลอดภัยไม่ปลอดภัย
คงที่
-
พยายาม
-
ฟิลด์ฟิลด์ = unsafe.class.getDeclaredField ("Theunsafe");
field.setAccessible (จริง);
ไม่ปลอดภัย = (ไม่ปลอดภัย) ฟิลด์ get (null);
-
จับ (ข้อยกเว้น e)
-
โยน RuntimeException ใหม่ (E);
-
-
LongArrayOffset LongArrayOffset ส่วนตัว = unsafe.arraybaseoffset (ยาว []. คลาส);
ผลลัพธ์ผลลัพธ์คือ:
การคัดลอกรหัสมีดังนี้:
ก่อนเปลี่ยน = fffffffffffffffffff
หลังจากการเปลี่ยนแปลง: i = 0, val = ffffffffffffff00
หลังจากการเปลี่ยนแปลง: i = 1, val = ffffffffffff0000
หลังจากการเปลี่ยนแปลง: i = 2, val = fffffffffff000000
หลังจากการเปลี่ยนแปลง: i = 3, val = ffffffff00000000
หลังจากการเปลี่ยนแปลง: i = 4, val = ffffff00000000000
หลังจากการเปลี่ยนแปลง: i = 5, val = ffff0000000000000
หลังจากการเปลี่ยนแปลง: i = 6, val = ff000000000000000
หลังจากการเปลี่ยนแปลง: i = 7, val = 0
3. การจัดสรรหน่วยความจำของ sun.misc.unsafe
ดังที่ได้กล่าวมาแล้วในชวาบริสุทธิ์ขนาดหน่วยความจำที่เราสามารถจัดสรรได้นั้นมี จำกัด ข้อ จำกัด นี้ถูกตั้งค่าในเวอร์ชันเริ่มต้นของ Java และในเวลานั้นผู้คนไม่กล้าแบ่งปันความทรงจำหลาย G เช่นนี้ แต่ตอนนี้มันเป็นยุคของข้อมูลขนาดใหญ่และเราต้องการหน่วยความจำมากขึ้น ใน Java มีสองวิธีในการรับความทรงจำเพิ่มเติม:
1. จัดสรรหน่วยความจำขนาดเล็กจำนวนมากจากนั้นใช้อย่างมีเหตุผลเป็นหน่วยความจำขนาดใหญ่อย่างต่อเนื่อง
2. ใช้ sun.misc.unsafe.allcateMemory (ยาว) เพื่อจัดสรรหน่วยความจำ
วิธีแรกนั้นน่าสนใจกว่าเล็กน้อยจากมุมมองของอัลกอริทึมดังนั้นลองมาดูวิธีที่สอง
sun.misc.unsafe จัดเตรียมชุดของวิธีการจัดสรรจัดสรรใหม่และปล่อยหน่วยความจำ พวกเขาคล้ายกับวิธี malloc/ฟรีของ C:
1. unsafe.locatememory (ขนาดยาว)-Allocate ชิ้นส่วนของพื้นที่หน่วยความจำ หน่วยความจำชิ้นนี้อาจมีข้อมูลขยะ (ไม่ล้างโดยอัตโนมัติ) หากการจัดสรรล้มเหลวยกเว้น java.lang.outofmemoryError จะถูกโยนลงไป มันส่งคืนที่อยู่หน่วยความจำที่ไม่ใช่ศูนย์ (ดูคำอธิบายด้านล่าง)
2.Unsafe.reallocateMemory (ที่อยู่ยาว, ขนาดยาว)-reallocate ชิ้นส่วนของหน่วยความจำและคัดลอกข้อมูลจากบัฟเฟอร์หน่วยความจำเก่า (ที่ที่อยู่ชี้ไปที่) บล็อกหน่วยความจำที่จัดสรรใหม่ หากที่อยู่เท่ากับ 0 วิธีนี้มีผลเช่นเดียวกับ allocatememory มันส่งคืนที่อยู่ของบัฟเฟอร์หน่วยความจำใหม่
3.Ansafe.FreeMemory (ที่อยู่ยาว)-ฟรีบัฟเฟอร์หน่วยความจำที่สร้างขึ้นโดยสองวิธีก่อนหน้า หากที่อยู่คือ 0 อย่าทำอะไรเลย
หน่วยความจำที่จัดสรรโดยวิธีการเหล่านี้ควรใช้ในโหมดที่เรียกว่าที่อยู่การลงทะเบียนเดียว: UNSAFE จัดเตรียมชุดของวิธีการที่ยอมรับพารามิเตอร์ที่อยู่เดียวเท่านั้น (ซึ่งแตกต่างจากโหมดการลงทะเบียนคู่พวกเขาต้องการวัตถุและออฟเซ็ตชดเชย) หน่วยความจำที่จัดสรรด้วยวิธีนี้อาจมีขนาดใหญ่กว่าสิ่งที่คุณกำหนดค่าในพารามิเตอร์ Java ของ -xmx
หมายเหตุ: หน่วยความจำที่จัดสรรโดย Unsafe ไม่สามารถเก็บขยะได้ คุณต้องถือว่าเป็นทรัพยากรปกติและจัดการด้วยตัวเอง
นี่คือตัวอย่างของการใช้ UNSARE.ALLOCATEMEMORY เพื่อจัดสรรหน่วยความจำและยังตรวจสอบว่าบัฟเฟอร์หน่วยความจำทั้งหมดสามารถอ่านและเขียนได้หรือไม่:
การคัดลอกรหัสมีดังนี้:
ขนาด int สุดท้าย = integer.max_value / 2;
addr ยาว final = unsafe.allocatememory (ขนาด);
พยายาม
-
System.out.println ("ที่อยู่ที่ไม่ปลอดภัย =" + addr);
สำหรับ (int i = 0; i <size; ++ i)
-
unsafe.putByte (addr + i, (byte) 123);
ถ้า (unsafe.getByte (addr + i)! = 123)
System.out.println ("ล้มเหลวที่ Offset =" + i);
-
-
ในที่สุด
-
unsafe.freememory (addr);
-
อย่างที่คุณเห็นการใช้ Sun.misc.unsafe คุณสามารถเขียนรหัสการเข้าถึงหน่วยความจำทั่วไปได้มาก: ไม่ว่าจะจัดสรรหน่วยความจำประเภทใดใน Java คุณสามารถอ่านและเขียนข้อมูลประเภทใดก็ได้