Java Reflection Performance


172

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


คำตอบ:


169

ใช่ - อย่างแน่นอน การมองหาชั้นเรียนผ่านการสะท้อนนั้นมีขนาดใหญ่ขึ้น

การอ้างถึงเอกสารของ Java เกี่ยวกับการสะท้อน :

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

นี่คือการทดสอบง่ายๆที่ฉันแฮ็คในเครื่องของฉันภายใน 5 นาทีโดยเรียกใช้ Sun JRE 6u10:

public class Main {

    public static void main(String[] args) throws Exception
    {
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = new A();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void doReflection() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = (A) Class.forName("misc.A").newInstance();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }
}

ด้วยผลลัพธ์เหล่านี้:

35 // no reflection
465 // using reflection

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

แม้ว่าคุณจะยกตัวอย่าง แต่คุณก็ยังได้รับความนิยม:

30 // no reflection
47 // reflection using one lookup, only instantiating

อีกครั้ง YMMV


5
ในเครื่องของฉันการเรียก. newInstance () ที่มีการเรียก Class.forName () เพียงครั้งเดียวจะได้คะแนน 30 หรือมากกว่านั้น ขึ้นอยู่กับรุ่นของ VM ความแตกต่างอาจใกล้เคียงกว่าที่คุณคิดด้วยกลยุทธ์การแคชที่เหมาะสม
ฌอน Reilly

56
@Peter Lawrey ด้านล่างชี้ให้เห็นว่าการทดสอบนี้ไม่ถูกต้องอย่างสมบูรณ์เพราะคอมไพเลอร์ได้ทำการปรับปรุงโซลูชันที่ไม่สะท้อนแสง (มันสามารถพิสูจน์ได้ว่าไม่มีสิ่งใดที่ทำและปรับให้เหมาะสมสำหรับลูป) จำเป็นต้องทำงานซ้ำและควรลบออกจาก SO เนื่องจากข้อมูลไม่ถูกต้อง / ทำให้เข้าใจผิด แคชวัตถุที่สร้างขึ้นในอาร์เรย์ในทั้งสองกรณีเพื่อป้องกันไม่ให้เครื่องมือเพิ่มประสิทธิภาพจากการเพิ่มประสิทธิภาพออก (มันไม่สามารถทำได้ในสถานการณ์ไตร่ตรองเพราะมันไม่สามารถพิสูจน์ได้ว่าตัวสร้างไม่มีผลข้างเคียง)
Bill K

6
@Bill K - ไปกันเถอะ ใช่ตัวเลขถูกปิดเนื่องจากการปรับให้เหมาะสม ไม่การทดสอบไม่ถูกต้องสมบูรณ์ ฉันได้เพิ่มการโทรที่จะขจัดความเป็นไปได้ที่จะบิดเบือนผลและตัวเลขยังคงซ้อนกับการสะท้อน ไม่ว่าในกรณีใดโปรดจำไว้ว่านี่เป็นเกณฑ์มาตรฐานขนาดเล็กที่หยาบมากซึ่งเพิ่งแสดงให้เห็นว่าการสะท้อนนั้นมีค่าโสหุ้ยเสมอ
Yuval Adam

4
นี่อาจเป็นมาตรฐานที่ไร้ประโยชน์ ขึ้นอยู่กับสิ่งที่ทำ หากไม่ทำอะไรเลยที่มีผลข้างเคียงที่มองเห็นได้มาตรฐานของคุณจะทำงานเฉพาะรหัสที่ตาย
nes1983

9
ฉันเพิ่งเห็น JVM เพิ่มประสิทธิภาพการสะท้อน 35 เท่า การรันการทดสอบซ้ำ ๆ ในลูปคือวิธีที่คุณทดสอบโค้ดที่ปรับให้เหมาะสม การวนซ้ำครั้งแรก: 3045ms, การทำซ้ำครั้งที่สอง: 2941 มิลลิวินาที, การทำซ้ำครั้งที่สาม: 90ms, การทำซ้ำครั้งที่สี่: 83 มิลลิวินาที รหัส: c.newInstance (i) c เป็นตัวสร้าง รหัสที่ไม่ใช่ไตร่ตรอง: ใหม่ A (i) ซึ่งให้ผล 13, 4, 3 .. ms คูณ ใช่แล้วการไตร่ตรองในกรณีนี้ช้า แต่ไม่ช้าลงเกือบเท่าสิ่งที่ผู้คนสรุปเพราะทุกการทดสอบที่ฉันเห็นพวกเขากำลังทำการทดสอบเพียงครั้งเดียวโดยไม่ให้โอกาสแก่ JVM ในการแทนที่รหัสไบต์ด้วยเครื่อง รหัส.
Mike

87

ใช่มันช้าลง

แต่จำกฎ damn # 1 - การเพิ่มประสิทธิภาพก่อนกำหนดเป็นรากของความชั่วร้ายทั้งหมด

(อาจผูกกับ # 1 สำหรับ DRY)

ฉันสาบานถ้ามีคนมาหาฉันที่ทำงานและถามฉันว่าฉันจะระวังตัวรหัสของพวกเขาในอีกไม่กี่เดือนข้างหน้า

คุณต้องไม่ปรับให้เหมาะสมจนกว่าคุณจะแน่ใจว่าคุณต้องการมันจนกว่าจะถึงตอนนั้นเพียงแค่เขียนโค้ดที่ดีและอ่านได้

โอ้และฉันไม่ได้หมายถึงเขียนโค้ดโง่เช่นกัน แค่คิดเกี่ยวกับวิธีที่สะอาดที่สุดที่คุณสามารถทำได้ - ไม่มีการคัดลอกและวาง ฯลฯ (ยังต้องระวังสิ่งต่าง ๆ เช่นลูปด้านในและการใช้คอลเลกชันที่เหมาะกับความต้องการของคุณมากที่สุด - ไม่สนใจการเขียนโปรแกรมเหล่านี้ มันคือการเขียนโปรแกรม "ไม่ดี")

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

แก้ไข:

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

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


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

26
-1: การหลีกเลี่ยงการทำสิ่งต่าง ๆ ในทางที่ผิดไม่ใช่การเพิ่มประสิทธิภาพ แต่เป็นเพียงการทำสิ่งต่าง ๆ การปรับให้เหมาะสมกำลังทำสิ่งที่ผิดวิธีที่ซับซ้อนเนื่องจากความกังวลเรื่องประสิทธิภาพหรือจินตภาพ
soru

5
@soru เห็นด้วยทั้งหมด การเลือกรายการที่ถูกเชื่อมโยงเหนือรายการอาร์เรย์สำหรับการเรียงลำดับการแทรกเป็นเพียงวิธีที่เหมาะสมในการทำสิ่งต่าง ๆ แต่คำถามเฉพาะนี้ - มีกรณีการใช้งานที่ดีสำหรับทั้งสองด้านของคำถามเดิมดังนั้นการเลือกคำถามตามประสิทธิภาพแทนที่จะเป็นโซลูชันที่ใช้งานได้ดีที่สุดจะผิด ฉันไม่แน่ใจว่าเราไม่เห็นด้วยเลยดังนั้นฉันไม่แน่ใจว่าทำไมคุณถึงพูดว่า "-1"
Bill K

14
โปรแกรมเมอร์นักวิเคราะห์ที่มีเหตุผลจำเป็นต้องพิจารณาประสิทธิภาพในระยะแรก ๆ หรือคุณอาจจะจบลงด้วยระบบที่ไม่สามารถปรับให้เหมาะสมในช่วงเวลาที่มีประสิทธิภาพและคุ้มค่า ไม่คุณไม่ได้ปรับให้เหมาะสมกับทุกรอบสัญญาณนาฬิกา แต่แน่นอนที่สุดคุณจะใช้แนวทางปฏิบัติที่ดีที่สุดสำหรับบางสิ่งที่เป็นพื้นฐานในการสร้างคลาส ตัวอย่างนี้เป็นตัวอย่างที่ยอดเยี่ยมที่คุณพิจารณาคำถามดังกล่าวเกี่ยวกับการสะท้อนกลับ มันจะเป็นโปรแกรมเมอร์ที่น่าสงสารคนหนึ่งที่เดินหน้าและใช้การไตร่ตรองตลอดทั้งระบบล้านเส้นในภายหลังเพื่อค้นพบว่ามันเป็นคำสั่งที่มีขนาดช้าเกินไป
RichieHH

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

36

คุณอาจพบว่า A = new A () กำลังได้รับการปรับปรุงโดย JVM หากคุณวางวัตถุลงในอาร์เรย์สิ่งเหล่านั้นจะทำงานได้ไม่ดีนัก ;) ภาพพิมพ์ต่อไปนี้ ...

new A(), 141 ns
A.class.newInstance(), 266 ns
new A(), 103 ns
A.class.newInstance(), 261 ns

public class Run {
    private static final int RUNS = 3000000;

    public static class A {
    }

    public static void main(String[] args) throws Exception {
        doRegular();
        doReflection();
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = new A();
        }
        System.out.printf("new A(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }

    public static void doReflection() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = A.class.newInstance();
        }
        System.out.printf("A.class.newInstance(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }
}

สิ่งนี้ชี้ให้เห็นความแตกต่างคือประมาณ 150 ns บนเครื่องของฉัน


ดังนั้นคุณเพิ่งฆ่าเครื่องมือเพิ่มประสิทธิภาพดังนั้นตอนนี้ทั้งสองรุ่นจะช้า การสะท้อนกลับจึงยังคงช้าอยู่
gbjbaanb

13
@gbjbaanb หากเครื่องมือเพิ่มประสิทธิภาพได้ปรับการสร้างให้เหมาะสมที่สุดแล้วมันไม่ใช่การทดสอบที่ถูกต้อง การทดสอบของ @ Peter นั้นใช้ได้จริงเพราะมันเปรียบเทียบเวลาการสร้างจริง (เครื่องมือเพิ่มประสิทธิภาพจะไม่สามารถทำงานในสถานการณ์ใด ๆ ในโลกแห่งความเป็นจริงใด ๆ เพราะในสถานการณ์จริงที่คุณต้องการวัตถุที่คุณกำลังอินสแตนซ์)
Bill K

10
@ nes1983 ในกรณีนี้คุณอาจมีโอกาสสร้างมาตรฐานที่ดีกว่า บางทีคุณสามารถเสนอสิ่งที่สร้างสรรค์เช่นสิ่งที่ควรอยู่ในเนื้อหาของวิธีการ
Peter Lawrey

1
บน mac ของฉัน openjdk 7u4 ความแตกต่างคือ 95ns เทียบกับ 100ns แทนที่จะเก็บ A ไว้ในอาร์เรย์ฉันเก็บ hashCodes ถ้าคุณบอกว่า -verbose: class คุณสามารถดูว่าฮอตสปอตสร้าง bytecode สำหรับการสร้าง A และการเพิ่มความเร็ว
Ron

@PeterLawrey ถ้าฉันค้นหาครั้งเดียว (โทรเดียวClass.getDeclaredMethod) แล้วโทรMethod.invokeหลาย ๆ ครั้ง? ฉันใช้การสะท้อนหนึ่งครั้งหรือหลายครั้งตามที่ฉันเรียกใช้หรือไม่ คำถามติดตามจะเกิดอะไรขึ้นถ้าแทนที่จะMethodเป็นConstructorและฉันทำConstructor.newInstanceหลาย ๆ ครั้ง?
tmj

28

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

ตัวอย่างแอปพลิเคชันที่ใช้การสร้างรหัส:

  • วิธีการเรียกใช้บนพร็อกซีที่สร้างโดยCGLIBนั้นเร็วกว่าพร็อกซีแบบไดนามิกของ Java เล็กน้อยเนื่องจาก CGLIB สร้าง bytecode สำหรับพร็อกซีของตน แต่พร็อกซีแบบไดนามิกใช้การสะท้อนเท่านั้น ( ฉันวัด CGLIB

  • JSerialสร้าง bytecode สำหรับการอ่าน / เขียนฟิลด์ของวัตถุที่เป็นอนุกรมแทนที่จะใช้การสะท้อน มีมาตรฐานบางอย่างในเว็บไซต์ของ JSerial

  • ฉันไม่แน่ใจ 100% (และฉันไม่รู้สึกอยากอ่านที่มาตอนนี้) แต่ฉันคิดว่าGuiceสร้าง bytecode เพื่อทำการฉีดพึ่งพา ช่วยแก้ให้ด้วยนะถ้าฉันผิด.


27

"สำคัญ" ขึ้นอยู่กับบริบททั้งหมด

หากคุณกำลังใช้การสะท้อนเพื่อสร้างวัตถุตัวจัดการเดียวโดยยึดตามแฟ้มการกำหนดค่าบางอย่างจากนั้นใช้เวลาที่เหลือในการรันการสืบค้นฐานข้อมูล หากคุณกำลังสร้างวัตถุจำนวนมากผ่านการสะท้อนในวงแคบ ๆ แล้วใช่มันมีความสำคัญ

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


24

มีค่าใช้จ่ายที่มีการสะท้อน แต่มันมีขนาดเล็กกว่าบน VMs ที่ทันสมัยกว่าที่เคยเป็น

หากคุณใช้การไตร่ตรองเพื่อสร้างทุกวัตถุอย่างง่ายในโปรแกรม ใช้เป็นครั้งคราวเมื่อคุณมีเหตุผลที่ดีไม่ควรเป็นปัญหาเลย


11

ใช่มีประสิทธิภาพในการทำงานเมื่อใช้ Reflection แต่วิธีแก้ไขที่เป็นไปได้สำหรับการปรับให้เหมาะสมคือการแคชเมธอด:

  Method md = null;     // Call while looking up the method at each iteration.
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md = ri.getClass( ).getMethod("getValue", null);
        md.invoke(ri, null);
      }

      System.out.println("Calling method " + CALL_AMOUNT+ " times reflexively with lookup took " + (System.currentTimeMillis( ) - millis) + " millis");



      // Call using a cache of the method.

      md = ri.getClass( ).getMethod("getValue", null);
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md.invoke(ri, null);
      }
      System.out.println("Calling method " + CALL_AMOUNT + " times reflexively with cache took " + (System.currentTimeMillis( ) - millis) + " millis");

จะส่งผลให้:

[java] วิธีการโทร 1000000 ครั้งด้วยการค้นหาอย่างละเอียดใช้เวลา 5,618 มิลลิวินาที

[java] วิธีการโทร 1000000 ครั้งโดยใช้แคชแคช 270 มิลลิวินาที


การนำวิธีการ / คอนสตรัคเตอร์กลับมาใช้ใหม่มีประโยชน์และช่วยได้จริง แต่โปรดทราบว่าการทดสอบข้างต้นไม่ได้ให้ตัวเลขที่มีความหมายเนื่องจากปัญหาการเปรียบเทียบ (ไม่มีการวอร์มอัพดังนั้นการวนรอบแรกโดยเฉพาะ
StaxMan

7

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

ระวัง microbenchmarks ในหัวข้อนี้มีข้อบกพร่องอย่างลึกดังนั้นใช้พวกเขาด้วยเม็ดเกลือ ข้อบกพร่องเล็ก ๆ น้อย ๆ ของปีเตอร์ลอว์เรย์คือ: มันอุ่นเครื่องเพื่อให้ได้วิธีการที่เหมาะสมและมัน (มีสติ) เอาชนะการวิเคราะห์ทางหนีเพื่อให้แน่ใจว่าการจัดสรรจะเกิดขึ้นจริง แม้ว่าจะมีปัญหาก็ตามตัวอย่างเช่นจำนวนร้านค้าอาร์เรย์จำนวนมากสามารถคาดหวังว่าจะเอาชนะแคชและบัฟเฟอร์ร้านค้าดังนั้นสิ่งนี้จะกลายเป็นมาตรฐานหน่วยความจำส่วนใหญ่หากการจัดสรรของคุณเร็วมาก (ความรุ่งโรจน์ถึง Peter ในการได้ข้อสรุปว่า: ความแตกต่างคือ "150ns" มากกว่า "2.5x" ฉันสงสัยว่าเขาทำสิ่งนี้เพื่อหาเลี้ยงชีพ)


7

ที่น่าสนใจคือการตั้งค่า setAccessible (true) ซึ่งข้ามการตรวจสอบความปลอดภัยมีค่าใช้จ่ายลดลง 20%

ไม่มี setAccessible (จริง)

new A(), 70 ns
A.class.newInstance(), 214 ns
new A(), 84 ns
A.class.newInstance(), 229 ns

ด้วย setAccessible (จริง)

new A(), 69 ns
A.class.newInstance(), 159 ns
new A(), 85 ns
A.class.newInstance(), 171 ns

1
ดูเหมือนจะชัดเจนในหลักการ ตัวเลขเหล่านี้มีขนาดเป็นเส้นตรงเมื่อเรียกใช้1000000การร้องขอหรือไม่
Lukas Eder

โดยทั่วไปแล้วsetAccessible()จะมีความแตกต่างได้มากขึ้นโดยเฉพาะอย่างยิ่งสำหรับวิธีการที่มีอาร์กิวเมนต์หลายตัวดังนั้นจึงควรเรียกใช้เสมอ
StaxMan

6

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


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

เช่นไลบรารีที่ใช้ AOP นั้นต้องการการสะท้อนกลับ
gaurav

4

ใน doReflection () เป็นค่าใช้จ่ายเนื่องจาก Class.forName ("misc.A") (ที่จะต้องมีการค้นหาชั้นเรียนอาจสแกนเส้นทางคลาสบนระบบไฟล์) แทนที่จะเรียก newInstance () บนคลาส ฉันสงสัยว่าสถิติจะเป็นอย่างไรถ้า Class.forName ("misc.A") ทำเพียงครั้งเดียวนอกวง for-loop มันไม่จำเป็นต้องทำทุกครั้งที่มีการวนซ้ำ


1

ใช่จะทำให้การสร้างวัตถุช้าลงโดยการไตร่ตรองเพราะ JVM ไม่สามารถปรับรหัสให้เหมาะกับเวลาในการรวบรวมได้ ดูบทเรียนของ Sun / Java Reflectionสำหรับรายละเอียดเพิ่มเติม

ดูการทดสอบอย่างง่ายนี้:

public class TestSpeed {
    public static void main(String[] args) {
        long startTime = System.nanoTime();
        Object instance = new TestSpeed();
        long endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");

        startTime = System.nanoTime();
        try {
            Object reflectionInstance = Class.forName("TestSpeed").newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");
    }
}

3
โปรดทราบว่าคุณควรแยกการค้นหา ( Class.forName()) ออกจาก instanciation (newInstance ()) เนื่องจากมีความแตกต่างอย่างมากในลักษณะการทำงานและคุณสามารถหลีกเลี่ยงการค้นหาซ้ำ ๆ ในระบบที่ออกแบบมาอย่างดี
Joachim Sauer

3
นอกจากนี้: คุณต้องดำเนินการแต่ละงานหลาย ๆ ครั้งเพื่อรับเกณฑ์มาตรฐานที่มีประโยชน์: ก่อนอื่นการกระทำนั้นช้าเกินไปที่จะวัดได้อย่างน่าเชื่อถือและอย่างที่สองคุณจะต้องอุ่นเครื่อง HotSpot VM เพื่อรับตัวเลขที่มีประโยชน์
Joachim Sauer

1

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


0

ฉันคิดว่ามันขึ้นอยู่กับว่าเป้าหมายนั้นเบาหรือหนักแค่ไหน ถ้าวิธีเป้าหมายเบามาก (เช่น getter / setter) มันอาจช้ากว่า 1 ~ 3 เท่า หากวิธีเป้าหมายใช้เวลาประมาณ 1 มิลลิวินาทีหรือมากกว่านั้นประสิทธิภาพจะใกล้เคียงกันมาก นี่คือการทดสอบที่ฉันทำกับ Java 8 และการสะท้อนกลับ :

public class ReflectionTest extends TestCase {    
    @Test
    public void test_perf() {
        Profiler.run(3, 100000, 3, "m_01 by refelct", () -> Reflection.on(X.class)._new().invoke("m_01")).printResult();    
        Profiler.run(3, 100000, 3, "m_01 direct call", () -> new X().m_01()).printResult();    
        Profiler.run(3, 100000, 3, "m_02 by refelct", () -> Reflection.on(X.class)._new().invoke("m_02")).printResult();    
        Profiler.run(3, 100000, 3, "m_02 direct call", () -> new X().m_02()).printResult();    
        Profiler.run(3, 100000, 3, "m_11 by refelct", () -> Reflection.on(X.class)._new().invoke("m_11")).printResult();    
        Profiler.run(3, 100000, 3, "m_11 direct call", () -> X.m_11()).printResult();    
        Profiler.run(3, 100000, 3, "m_12 by refelct", () -> Reflection.on(X.class)._new().invoke("m_12")).printResult();    
        Profiler.run(3, 100000, 3, "m_12 direct call", () -> X.m_12()).printResult();
    }

    public static class X {
        public long m_01() {
            return m_11();
        }    
        public long m_02() {
            return m_12();
        }    
        public static long m_11() {
            long sum = IntStream.range(0, 10).sum();
            assertEquals(45, sum);
            return sum;
        }    
        public static long m_12() {
            long sum = IntStream.range(0, 10000).sum();
            assertEquals(49995000, sum);
            return sum;
        }
    }
}

รหัสทดสอบฉบับสมบูรณ์มีให้ที่ GitHub: ReflectionTest.java

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