แลมบ์ดานิพจน์สร้างวัตถุบนฮีปทุกครั้งที่ถูกประมวลผลหรือไม่?


182

เมื่อฉันทำซ้ำคอลเลกชันโดยใช้น้ำตาล syntactic ใหม่ของ Java 8 เช่น

myStream.forEach(item -> {
  // do something useful
});

นี่ไม่เทียบเท่ากับตัวอย่าง 'ไวยากรณ์เก่า' ด้านล่างใช่หรือไม่

myStream.forEach(new Consumer<Item>() {
  @Override
  public void accept(Item item) {
    // do something useful
  }
});

สิ่งนี้หมายความว่ามีการสร้างConsumerวัตถุนิรนามใหม่บน heap ทุกครั้งที่ทำซ้ำคอลเลกชันหรือไม่ ใช้พื้นที่ของกองมากเท่าไร ความหมายของประสิทธิภาพการทำงานมีอะไรบ้าง มันหมายความว่าฉันควรใช้รูปแบบเก่าสำหรับลูปเมื่อวนซ้ำโครงสร้างข้อมูลหลายระดับขนาดใหญ่หรือไม่?


48
คำตอบสั้น ๆ : ไม่ สำหรับลูกแกะไร้สัญชาติ (คนที่ไม่จับอะไรจากบริบทของคำศัพท์) จะมีเพียงตัวอย่างเดียวเท่านั้นที่จะถูกสร้างขึ้น (ขี้เกียจ) และเก็บไว้ที่ไซต์จับภาพ (นี่คือวิธีการใช้งานการทำงานข้อมูลจำเพาะถูกเขียนอย่างละเอียดเพื่อให้ แต่ไม่จำเป็นต้องใช้วิธีการนี้)
Brian Goetz

คำตอบ:


158

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

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

คุณสามารถอ่านคำตอบนี้สำหรับรายละเอียดเพิ่มเติม มีฉันไม่เพียง แต่ให้คำอธิบายรายละเอียดเพิ่มเติม แต่ยังทดสอบรหัสเพื่อสังเกตพฤติกรรมปัจจุบัน


ข้อมูลนี้ครอบคลุมโดยข้อกำหนดภาษาJava®บทที่“ 15.27.4 การประเมินเวลาดำเนินการของแลมบ์ดานิพจน์

สรุป:

กฎเหล่านี้มีจุดประสงค์เพื่อให้เกิดความยืดหยุ่นในการนำไปใช้ของภาษาการเขียนโปรแกรม Java ซึ่ง ได้แก่ :

  • ไม่จำเป็นต้องจัดสรรวัตถุใหม่ในทุกการประเมินผล

  • วัตถุที่สร้างขึ้นโดยการแสดงออกแลมบ์ดาที่แตกต่างกันไม่จำเป็นต้องอยู่ในชั้นเรียนที่แตกต่างกัน (ตัวอย่างเช่นถ้าวัตถุเหมือนกัน)

  • ทุกวัตถุที่สร้างโดยการประเมินผลไม่จำเป็นต้องอยู่ในคลาสเดียวกัน (เช่นตัวแปรโลคอลที่จับได้อาจถูกขีดเส้นใต้เป็นต้น)

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


24

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

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

นี่เป็นบทความเชิงลึกที่ดีและเข้าถึงได้ในหัวข้อ:

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood


0

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

ดังนั้นหน่วยความจำที่ใช้โดยลูปจึงไม่ขึ้นกับขนาดของคอลเลกชัน

นี่ไม่เทียบเท่ากับตัวอย่าง 'ไวยากรณ์เก่า' หรือไม่

ใช่. มันมีความแตกต่างเล็กน้อยในระดับต่ำมาก แต่ฉันไม่คิดว่าคุณควรใส่ใจพวกเขา นิพจน์ Lamba ใช้คุณลักษณะ invokedynamic แทนคลาสที่ไม่ระบุชื่อ


คุณมีเอกสารที่ระบุสิ่งนี้หรือไม่? เป็นการเพิ่มประสิทธิภาพที่น่าสนใจทีเดียว
A. พระราม

ขอบคุณ แต่ถ้าฉันมีคอลเลกชันของคอลเลกชันตัวอย่างเช่นเมื่อทำการค้นหาเชิงลึกครั้งแรกในโครงสร้างข้อมูลต้นไม้?
Bastian Voigt

2
@ A.Rama ขออภัยฉันไม่เห็นการเพิ่มประสิทธิภาพ มันเป็นเหมือนกันโดยมีหรือไม่มี lambdas และมีหรือไม่มี forEach loop
aalku

1
มันไม่เหมือนกันจริง ๆ แต่ยังคงต้องใช้วัตถุมากที่สุดหนึ่งรายการต่อระดับการซ้อนในเวลาหนึ่งซึ่งไม่สำคัญ การวนซ้ำแบบใหม่ของลูปภายในจะสร้างวัตถุใหม่ซึ่งส่วนใหญ่อาจจะจับรายการปัจจุบันจากลูปภายนอก สิ่งนี้สร้างแรงกดดัน GC แต่ก็ยังไม่มีอะไรต้องกังวล
Marko Topolnik

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