우리는 일상 업무나 인터뷰에서 '반사'라는 지식 포인트를 자주 접하게 됩니다. '반사'를 통해 객체 정보를 동적으로 얻고 객체 메서드 등을 유연하게 호출할 수 있지만, 이를 사용하는 동안 이는 출현을 동반합니다. 다른 소리, 즉 "반사"는 매우 느리므로 자제해서 사용해야 합니다. 반사가 정말 느린가요? 우리가 일반적으로 객체를 생성하고 메소드를 호출할 때보다 얼마나 느린가요? 많은 사람들이 한번도 테스트해 본 적이 없고 그냥 "들은" 것뿐입니다. 몇 가지 테스트 케이스를 통해 직접적으로 "반사"를 경험해 보겠습니다.
Text
테스트 개체 준비
먼저 id 및 name 속성만 포함하는 테스트 클래스 TestUser와 해당 getter/setter를 정의합니다. method , 사용자 정의 sayHi 메소드도 있습니다.
public class TestUser { private Integer id; private String name; public String sayHi(){ return "hi"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
객체 100만 개 생성 테스트
// 通过普通方式创建TestUser对象@Testpublic void testCommon(){ long start = System.currentTimeMillis(); TestUser user = null; int i = 0; while(i<1000000){ ++i; user = new TestUser(); } long end = System.currentTimeMillis(); System.out.println("普通对象创建耗时:"+(end - start ) + "ms"); }//普通对象创建耗时:10ms
// 通过反射方式创建TestUser对象@Testpublic void testReflexNoCache() throws Exception { long start = System.currentTimeMillis(); TestUser user = null; int i = 0; while(i<1000000){ ++i; user = (TestUser) Class.forName("ReflexDemo.TestUser").newInstance(); } long end = System.currentTimeMillis(); System.out.println("无缓存反射创建对象耗时:"+(end - start ) + "ms"); }//无缓存反射创建对象耗时:926ms
위 두 가지 테스트 방법에서 작성자는 각각 5번씩 테스트하여 소요된 시간의 평균을 구했습니다. 출력 결과를 보면 하나는 10ms이고 다른 하나는 926ms인 것을 알 수 있습니다. 100만 개의 객체가 생성되면 반사가 실제로 약 90배 느려집니다. 뭐야? 격차가 그렇게 큰가요? 반사가 정말 그렇게 느린가요? 다음으로 저자는 반사 자세를 바꿔가며 계속해서 테스트를 하여 결과가 어떤지 살펴봅니다.
// 通过缓存反射方式创建TestUser对象@Testpublic void testReflexWithCache() throws Exception { long start = System.currentTimeMillis(); TestUser user = null; Class rUserClass = Class.forName("RefleDemo.TestUser"); int i = 0; while(i<1000000){ ++i; user = (TestUser) rUserClass.newInstance(); } long end = System.currentTimeMillis(); System.out.println("通过缓存反射创建对象耗时:"+(end - start ) + "ms"); }//通过缓存反射创建对象耗时:41ms
사실 코드를 통해 Class.forName 메소드가 더 시간이 많이 걸린다는 것을 알 수 있습니다. 실제로는 로컬 메소드를 호출하고 이를 통해 JVM이 지정된 항목을 찾아 로드하도록 요청합니다. 수업. 따라서 프로젝트에서 이를 사용할 때 Class.forName이 반환한 Class 객체를 캐시하고, 다음 사용 시 캐시에서 직접 얻을 수 있어 클래스 획득 효율성이 크게 향상됩니다. 같은 방식으로 생성자, 메소드 및 기타 객체를 얻을 때 사용할 때마다 생성하는 데 시간이 많이 걸리는 것을 피하기 위해 캐시할 수도 있습니다.
반사 호출 메서드 테스트
@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis(); Class testUserClass = Class.forName("RefleDemo.TestUser"); TestUser testUser = (TestUser) testUserClass.newInstance(); Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){ ++i; method.invoke(testUser); } long end = System.currentTimeMillis(); System.out.println("反射调用方法耗时:"+(end - start ) + "ms"); }//反射调用方法耗时:330ms
@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis(); Class testUserClass = Class.forName("RefleDemo.TestUser"); TestUser testUser = (TestUser) testUserClass.newInstance(); Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){ ++i; method.setAccessible(true); method.invoke(testUser); } long end = System.currentTimeMillis(); System.out.println("setAccessible=true 反射调用方法耗时:"+(end - start ) + "ms"); }//setAccessible=true 反射调用方法耗时:188ms
여기서 sayHi 메서드를 1억 번 반영하고 호출한 후, 찾았습니다. Half 가 거의 빠릅니다. API를 보면 획득 필드 및 호출 메서드를 설정할 때 jdk가 보안 액세스 검사를 수행하고 이러한 작업에는 시간이 많이 걸리므로 setAccessible(true)를 통해 보안 검사를 해제할 수 있음을 알 수 있습니다. 반사 효율을 향상시킵니다.
ULTIMATE REFLECTION
위의 방법 외에 리플렉션을 극한까지 활용하는 방법이 있나요? 여기서는 고성능 반사 툴킷인 ReflectASM을 소개합니다. 바이트코드 생성을 통해 구현된 리플렉션 메커니즘입니다. 다음은 Java 리플렉션과의 성능 비교입니다.
결론
마지막으로 정리하면 Reflection을 더 잘 활용하기 위해서는 프로젝트 시작 시 관련 설정과 Reflection에 필요한 데이터를 메모리에 로드해야 한다#🎜 🎜 # 실행 단계 동안 이러한 메타데이터는 반사 작업을 위해 캐시에서 가져옵니다. 반영을 두려워할 필요는 없습니다. 올바른 방법을 사용하는 한 가상 머신은 "소문"만큼 느리지 않습니다. 타사 패키지를 사용하여 직접 코드 작업을 수행합니다.
관련 튜토리얼:위 내용은 Java 리플렉션 사용의 효율성을 높이는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!