การกระจายตัวของวัตถุขนาดใหญ่


97

แอปพลิเคชัน C # /. NET ที่ฉันกำลังทำงานอยู่มีปัญหาหน่วยความจำรั่วช้า ฉันใช้ CDB กับ SOS เพื่อตรวจสอบสิ่งที่เกิดขึ้น แต่ดูเหมือนว่าข้อมูลจะไม่สมเหตุสมผลดังนั้นฉันหวังว่าคุณคนใดคนหนึ่งอาจเคยมีประสบการณ์นี้มาก่อน

แอปพลิเคชันกำลังทำงานบนเฟรมเวิร์ก 64 บิต มันกำลังคำนวณและจัดลำดับข้อมูลไปยังโฮสต์ระยะไกลอย่างต่อเนื่องและกำลังกดปุ่ม Large Object Heap (LOH) อยู่พอสมควร อย่างไรก็ตามวัตถุ LOH ส่วนใหญ่ที่ฉันคาดว่าจะเป็นแบบชั่วคราว: เมื่อการคำนวณเสร็จสมบูรณ์และถูกส่งไปยังโฮสต์ระยะไกลหน่วยความจำควรจะว่าง อย่างไรก็ตามสิ่งที่ฉันเห็นคืออาร์เรย์อ็อบเจ็กต์ (สด) จำนวนมากที่สอดแทรกด้วยบล็อกหน่วยความจำว่างเช่นการสุ่มเซกเมนต์จาก LOH:

0:000> !DumpHeap 000000005b5b1000  000000006351da10
         Address               MT     Size
...
000000005d4f92e0 0000064280c7c970 16147872
000000005e45f880 00000000001661d0  1901752 Free
000000005e62fd38 00000642788d8ba8     1056       <--
000000005e630158 00000000001661d0  5988848 Free
000000005ebe6348 00000642788d8ba8     1056
000000005ebe6768 00000000001661d0  6481336 Free
000000005f214d20 00000642788d8ba8     1056
000000005f215140 00000000001661d0  7346016 Free
000000005f9168a0 00000642788d8ba8     1056
000000005f916cc0 00000000001661d0  7611648 Free
00000000600591c0 00000642788d8ba8     1056
00000000600595e0 00000000001661d0   264808 Free
...

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

นอกจากนี้โปรดทราบว่า CDB ไม่ได้รายงานประเภทเมื่อส่วนฮีปถูกทิ้ง: ฉันไม่แน่ใจว่าสิ่งนี้เกี่ยวข้องหรือไม่ หากฉันถ่ายโอนวัตถุที่ทำเครื่องหมาย (<-) CDB / SOS จะรายงานว่าใช้ได้:

0:015> !DumpObj 000000005e62fd38
Name: System.Object[]
MethodTable: 00000642788d8ba8
EEClass: 00000642789d7660
Size: 1056(0x420) bytes
Array: Rank 1, Number of elements 128, Type CLASS
Element Type: System.Object
Fields:
None

องค์ประกอบของอาร์เรย์อ็อบเจ็กต์คือสตริงทั้งหมดและสตริงนั้นสามารถจดจำได้จากโค้ดแอปพลิเคชันของเรา

นอกจากนี้ฉันไม่พบรูท GC ของพวกเขาเนื่องจากคำสั่ง! GCRoot หยุดทำงานและไม่กลับมาอีก (ฉันได้ลองทิ้งไว้ข้ามคืน)

ดังนั้นฉันจะขอบคุณมากถ้าใครสามารถส่องแสงว่าทำไมอาร์เรย์อ็อบเจ็กต์ขนาดเล็ก (<85k) เหล่านี้จึงลงเอยที่ LOH: สถานการณ์ใดที่. NET จะใส่อาร์เรย์อ็อบเจ็กต์ขนาดเล็กไว้ที่นั่น? นอกจากนี้ไม่มีใครรู้วิธีอื่นในการตรวจสอบรากเหง้าของวัตถุเหล่านี้?


อัปเดต 1

อีกทฤษฎีหนึ่งที่ฉันคิดขึ้นเมื่อวานนี้ก็คืออาร์เรย์อ็อบเจ็กต์เหล่านี้เริ่มมีขนาดใหญ่ แต่ถูกหดออกจากบล็อกของหน่วยความจำที่ว่างซึ่งเห็นได้ชัดในการทิ้งหน่วยความจำ สิ่งที่ทำให้ฉันสงสัยคืออาร์เรย์อ็อบเจ็กต์มักจะมีความยาว 1056 ไบต์ (128 องค์ประกอบ), 128 * 8 สำหรับการอ้างอิงและค่าโสหุ้ย 32 ไบต์

แนวคิดก็คือบางทีโค้ดที่ไม่ปลอดภัยในไลบรารีหรือใน CLR อาจทำให้จำนวนฟิลด์องค์ประกอบในส่วนหัวอาร์เรย์เสียหาย ใช้เวลานานหน่อยฉันรู้ ...


อัปเดต 2

ขอบคุณ Brian Rasmussen (ดูคำตอบที่ยอมรับ) ปัญหานี้ได้รับการระบุว่าเป็นการแยกส่วนของ LOH ที่เกิดจากตารางฝึกหัดสตริง! ฉันเขียนใบสมัครทดสอบด่วนเพื่อยืนยันสิ่งนี้:

static void Main()
{
    const int ITERATIONS = 100000;

    for (int index = 0; index < ITERATIONS; ++index)
    {
        string str = "NonInterned" + index;
        Console.Out.WriteLine(str);
    }

    Console.Out.WriteLine("Continue.");
    Console.In.ReadLine();

    for (int index = 0; index < ITERATIONS; ++index)
    {
        string str = string.Intern("Interned" + index);
        Console.Out.WriteLine(str);
    }

    Console.Out.WriteLine("Continue?");
    Console.In.ReadLine();
}

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

ในลูปที่สองสตริงที่ไม่ซ้ำกันจะถูกสร้างและอยู่ภายใน การดำเนินการนี้จะฝังรากไว้ในตารางฝึกงาน สิ่งที่ฉันไม่ทราบคือการแสดงตารางฝึกงานอย่างไร ดูเหมือนว่าจะประกอบด้วยชุดของเพจ - อาร์เรย์อ็อบเจ็กต์ 128 องค์ประกอบสตริง - ที่สร้างขึ้นใน LOH สิ่งนี้ชัดเจนมากขึ้นใน CDB / SOS:

0:000> .loadby sos mscorwks
0:000> !EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00f7a9b0
generation 1 starts at 0x00e79c3c
generation 2 starts at 0x00b21000
ephemeral segment allocation context: none
 segment    begin allocated     size
00b20000 00b21000  010029bc 0x004e19bc(5118396)
Large object heap starts at 0x01b21000
 segment    begin allocated     size
01b20000 01b21000  01b8ade0 0x00069de0(433632)
Total Size  0x54b79c(5552028)
------------------------------
GC Heap Size  0x54b79c(5552028)

การถ่ายโอนข้อมูลส่วน LOH เผยให้เห็นรูปแบบที่ฉันเห็นในแอปพลิเคชันที่รั่วไหล:

0:000> !DumpHeap 01b21000 01b8ade0
...
01b8a120 793040bc      528
01b8a330 00175e88       16 Free
01b8a340 793040bc      528
01b8a550 00175e88       16 Free
01b8a560 793040bc      528
01b8a770 00175e88       16 Free
01b8a780 793040bc      528
01b8a990 00175e88       16 Free
01b8a9a0 793040bc      528
01b8abb0 00175e88       16 Free
01b8abc0 793040bc      528
01b8add0 00175e88       16 Free    total 1568 objects
Statistics:
      MT    Count    TotalSize Class Name
00175e88      784        12544      Free
793040bc      784       421088 System.Object[]
Total 1568 objects

โปรดทราบว่าขนาดอาร์เรย์ออบเจ็กต์คือ 528 (แทนที่จะเป็น 1056) เนื่องจากเวิร์กสเตชันของฉันเป็น 32 บิตและแอ็พพลิเคชันเซิร์ฟเวอร์เป็น 64 บิต อาร์เรย์วัตถุยังคงมีความยาว 128 องค์ประกอบ

ดังนั้นคุณธรรมของเรื่องนี้คือการฝึกงานอย่างระมัดระวัง หากไม่ทราบว่าสตริงที่คุณกำลังฝึกงานเป็นสมาชิกของเซต จำกัด แอปพลิเคชันของคุณจะรั่วไหลเนื่องจากการแยกส่วนของ LOH อย่างน้อยในเวอร์ชัน 2 ของ CLR

ในกรณีของแอปพลิเคชันของเรามีรหัสทั่วไปในเส้นทางรหัส deserialisation ที่ฝึกงานตัวระบุเอนทิตีในระหว่างการยกเลิกการแช่ง: ตอนนี้ฉันสงสัยอย่างยิ่งว่านี่เป็นผู้กระทำผิด อย่างไรก็ตามความตั้งใจของผู้พัฒนานั้นดีอย่างชัดเจนเนื่องจากต้องการให้แน่ใจว่าหากเอนทิตีเดียวกันถูก deserialised หลายครั้งสตริงตัวระบุเพียงอินสแตนซ์เดียวจะถูกเก็บไว้ในหน่วยความจำ


2
คำถามที่ดี - ฉันสังเกตเห็นสิ่งเดียวกันในใบสมัครของฉัน วัตถุขนาดเล็กที่ถูกทิ้งไว้ใน LOH หลังจากทำความสะอาดบล็อกขนาดใหญ่แล้วและทำให้เกิดปัญหาการกระจายตัว
Reed Copsey

2
ฉันเห็นด้วยคำถามที่ดี ฉันจะคอยดูคำตอบ
Charlie Flowers

2
น่าสนใจมาก. ดูเหมือนว่าจะเป็นปัญหาในการดีบัก!
Matt Jordan

คำตอบ:


47

CLR ใช้ LOH เพื่อจัดสรรออบเจ็กต์บางอย่างล่วงหน้า (เช่นอาร์เรย์ที่ใช้สำหรับสตริงภายใน ) บางส่วนมีขนาดน้อยกว่า 85000 ไบต์ดังนั้นโดยปกติจะไม่ถูกจัดสรรบน LOH

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

นอกจากนี้เนื่องจากการเพิ่มประสิทธิภาพที่ค่อนข้างลึกลับdouble[]จึงมีการจัดสรรองค์ประกอบตั้งแต่ 1,000 รายการขึ้นไปบน LOH


อ็อบเจ็กต์ที่มีปัญหาคืออ็อบเจ็กต์ [] ที่มีการอ้างอิงถึงสตริงที่ฉันรู้ว่าโค้ดแอพสร้างขึ้น นี่หมายความว่าแอปกำลังสร้างออบเจ็กต์ [] (ฉันไม่เห็นหลักฐานเกี่ยวกับสิ่งนี้) หรือบางส่วนของ CLR (เช่นการทำให้เป็นอนุกรม) กำลังใช้พวกมันเพื่อทำงานกับวัตถุแอปพลิเคชัน
Paul Ruane

1
นั่นอาจเป็นโครงสร้างภายในที่ใช้สำหรับสตริงภายใน โปรดตรวจสอบคำตอบของฉันสำหรับคำถามนี้สำหรับรายละเอียดเพิ่มเติม: stackoverflow.com/questions/372547/…
Brian Rasmussen

อานี่เป็นโอกาสในการขายที่น่าสนใจมากขอบคุณ ลืมเรื่องโต๊ะฝึกงานไปโดยสิ้นเชิง ฉันรู้ว่าหนึ่งในนักพัฒนาของเราเป็นนักศึกษาฝึกงานที่กระตือรือร้นดังนั้นนี่คือสิ่งที่ฉันจะตรวจสอบอย่างแน่นอน
Paul Ruane

1
85000 ไบต์หรือ 84 * 1024 = 87040 ไบต์?
Peter Mortensen

5
85000 ไบต์ คุณสามารถตรวจสอบได้โดยสร้างอาร์เรย์ไบต์ 85000-12 (ขนาดความยาว MT บล็อกการซิงค์) และเรียกGC.GetGenerationใช้อินสแตนซ์ สิ่งนี้จะส่งคืน Gen2 - API ไม่แยกความแตกต่างระหว่าง Gen2 และ LOH ทำให้อาร์เรย์เล็กลงหนึ่งไบต์และ API จะส่งคืน Gen0
Brian Rasmussen

13

.NET Framework 4.5.1 มีความสามารถในการกระชับวัตถุฮีปขนาดใหญ่ (LOH) อย่างชัดเจนระหว่างการรวบรวมขยะ

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

ดูข้อมูลเพิ่มเติมในGCSettings.LargeObjectHeapCompactionMode


2

เมื่ออ่านคำอธิบายวิธีการทำงานของ GC และส่วนที่เกี่ยวกับการที่วัตถุมีอายุยืนยาวสิ้นสุดลงในรุ่นที่ 2 และคอลเลกชันของวัตถุ LOH เกิดขึ้นที่คอลเลกชันแบบเต็มเท่านั้น - เช่นเดียวกับคอลเลกชันของรุ่นที่ 2 ความคิดที่เกิดขึ้นในใจคือ .. ทำไมไม่เก็บเจเนอเรชั่นที่ 2 และวัตถุขนาดใหญ่ไว้ในฮีปเดียวกันด้วยล่ะ?

ถ้านั่นคือสิ่งที่เกิดขึ้นจริงมันจะอธิบายได้ว่าวัตถุขนาดเล็กมาอยู่ที่เดียวกับ LOH ได้อย่างไร - ถ้าพวกมันมีอายุยืนยาวพอที่จะจบลงในรุ่นที่ 2

ดังนั้นปัญหาของคุณดูเหมือนจะเป็นการโต้แย้งที่ดีกับความคิดที่เกิดขึ้นกับฉัน - มันจะส่งผลให้ LOH แตกกระจาย

สรุป: ปัญหาของคุณสามารถทำได้อธิบายได้ด้วย LOH และรุ่นที่ 2 ที่ใช้พื้นที่ฮีปเดียวกันแม้ว่าจะไม่มีข้อพิสูจน์ว่านี่คือคำอธิบายก็ตาม

อัปเดต:ผลลัพธ์ของการ!dumpheap -statเป่าทฤษฎีนี้ออกมาจากน้ำ! รุ่นที่ 2 และ LOH มีภูมิภาคของตัวเอง


ใช้! eeheap เพื่อแสดงเซ็กเมนต์ที่ประกอบกันเป็นฮีป Gen 0 และ gen 1 อาศัยอยู่ในเซ็กเมนต์เดียว (เซ็กเมนต์เดียวกัน) gen 2 และ LOH สามารถจัดสรรหลายเซ็กเมนต์ได้ แต่เซ็กเมนต์นั้นสำหรับแต่ละฮีพยังคงแยกกัน
Paul Ruane

ใช่เห็นแล้วขอบคุณ แค่อยากจะพูดถึงคำสั่ง! eeheaps เพราะมันแสดงพฤติกรรมนี้ในลักษณะที่ชัดเจนกว่ามาก
Paul Ruane

ประสิทธิภาพของ GC หลักส่วนใหญ่เกิดจากการที่มันสามารถย้ายวัตถุได้ดังนั้นจึงมีพื้นที่ว่างเพียงเล็กน้อยในหน่วยความจำบนฮีปหลัก หากวัตถุบนฮีปหลักถูกตรึงระหว่างการรวบรวมพื้นที่ด้านบนและด้านล่างของวัตถุที่ตรึงไว้อาจต้องถูกติดตามแยกกัน แต่เนื่องจากโดยปกติแล้วจำนวนของวัตถุที่ตรึงไว้จะมีขนาดเล็กมากดังนั้นจะเป็นจำนวนพื้นที่แยกต่างหากที่ GC ต้อง ติดตาม การผสมวัตถุที่เคลื่อนย้ายได้และไม่สามารถเคลื่อนย้ายได้ (ขนาดใหญ่) ในฮีปเดียวกันจะทำให้ประสิทธิภาพการทำงานลดลง
supercat

คำถามที่น่าสนใจกว่าคือทำไม. NET ทำให้doubleอาร์เรย์ที่มีขนาดใหญ่กว่า 1,000 องค์ประกอบบน LOH แทนที่จะปรับแต่ง GC เพื่อให้แน่ใจว่าพวกมันอยู่ในแนวเดียวกันกับขอบเขต 8 ไบต์ จริงๆแล้วแม้ในระบบ 32 บิตฉันคาดหวังว่าจะเป็นเพราะลักษณะการทำงานของแคชการจัดตำแหน่ง 8 ไบต์บนวัตถุทั้งหมดที่มีขนาดที่จัดสรรเป็นจำนวน 8 ไบต์อาจเป็นผลดีต่อประสิทธิภาพ มิฉะนั้นแม้ว่าประสิทธิภาพของdouble[]แคชที่ใช้งานหนักจะดีกว่าของที่ไม่มี แต่ฉันไม่รู้ว่าทำไมขนาดจึงสัมพันธ์กับการใช้งาน
supercat

@supercat นอกจากนี้กองทั้งสองยังทำงานแตกต่างกันมากในการจัดสรรเช่นกัน ฮีปหลักคือ (ในเวลานี้) โดยพื้นฐานแล้วจะเป็นสแต็กในรูปแบบการจัดสรรซึ่งจะจัดสรรที่ด้านบนเสมอโดยไม่สนใจพื้นที่ว่างใด ๆ - เมื่อมีการบดอัดช่องว่างจะถูกบีบออก สิ่งนี้ทำให้การจัดสรรแทบไม่ต้องดำเนินการใด ๆ และช่วยให้ข้อมูลท้องถิ่น ในทางกลับกันการจัดสรรบน LOH นั้นคล้ายกับวิธีการทำงานของ malloc นั่นคือจะพบจุดแรกที่สามารถเก็บสิ่งที่คุณกำลังจัดสรรได้และจัดสรรที่นั่น เนื่องจากมีไว้สำหรับอ็อบเจ็กต์ขนาดใหญ่จึงมีการกำหนดตำแหน่งข้อมูลและการลงโทษสำหรับการจัดสรรก็ไม่เลวร้ายเกินไป
Luaan

1

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

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

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


แก้ไข: ละเว้นขอบเขตหน่วยความจำและขนาดอาร์เรย์เฉพาะในขณะนี้: เพียงแค่คิดว่ากำลังทำอะไรกับสตริงเหล่านี้เพื่อทำให้เกิดการรั่วไหล ลองใช้! GCRoot เมื่อโปรแกรมของคุณสร้างหรือจัดการสตริงเหล่านี้เพียงครั้งหรือสองครั้งเมื่อมีวัตถุให้ติดตามน้อยลง


สตริงเป็นการผสมผสานระหว่าง Guids (ที่เราใช้) และคีย์สตริงซึ่งสามารถระบุตัวตนได้ทันที ฉันสามารถดูได้ว่าจะสร้างที่ไหน แต่จะไม่ถูกเพิ่ม (โดยตรง) ลงในอาร์เรย์ออบเจ็กต์และเราไม่ได้สร้างอาร์เรย์องค์ประกอบ 128 รายการอย่างชัดเจน อาร์เรย์ขนาดเล็กเหล่านี้ไม่ควรอยู่ใน LOH เพื่อเริ่มต้นด้วย
Paul Ruane

1

คำถามที่ดีฉันเรียนรู้จากการอ่านคำถาม

ฉันคิดว่าพา ธ โค้ด deserialisation ส่วนอื่น ๆ ก็ใช้ฮีปอ็อบเจ็กต์ขนาดใหญ่เช่นกันดังนั้นการแยกส่วน ถ้าสตริงทั้งหมดถูกผูกไว้ในเวลาเดียวกันฉันคิดว่าคุณจะโอเค

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

ส่วนใหญ่ฉันจะดูที่การเก็บตารางแฮชของสตริงสองสามรายการสุดท้ายที่คุณเคยเห็นและนำกลับมาใช้ใหม่ ด้วยการ จำกัด ขนาดตารางแฮชและส่งผ่านขนาดเมื่อคุณสร้างตารางคุณสามารถหยุดการกระจายตัวส่วนใหญ่ได้ จากนั้นคุณต้องมีวิธีลบสตริงที่คุณไม่เห็นเมื่อเร็ว ๆ นี้ออกจากตารางแฮชเพื่อ จำกัด ขนาด แต่ถ้าสตริงเส้นทางรหัส deserialisation ที่สร้างขึ้นนั้นสั้นอยู่แล้วคุณจะไม่ได้รับมากถ้ามีอะไร


1

ต่อไปนี้เป็นสองวิธีในการระบุcall-stackที่แน่นอนของการจัดสรรLOH

และเพื่อหลีกเลี่ยงการกระจายตัวของ LOH ให้จัดสรรอาร์เรย์ของออบเจ็กต์ขนาดใหญ่ล่วงหน้าและตรึงไว้ ใช้วัตถุเหล่านี้ซ้ำเมื่อจำเป็น นี่คือโพสต์เกี่ยวกับ LOH Fragmentation สิ่งนี้สามารถช่วยในการหลีกเลี่ยงการกระจายตัวของ LOH


ฉันไม่เห็นว่าทำไมการตรึงที่นี่จึงควรช่วยได้? วัตถุขนาดใหญ่ BTW บน LOH จะไม่ถูกย้ายโดย GC อยู่ดี รายละเอียดการใช้งานแม้ว่า
user492238

1

อ้างอิงจากhttp://webcache.googleusercontent.com/search?q=cache:l3Ppy_eFoAAJ:www.wintellect.com/devcenter/sloscialo/hey-who-stole-all-my-memory+&cd=5&hl=th&ct=clnk&gl= mx & ไคลเอนต์ = firefox-b

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

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