1. Trang chủ
  2. » Công Nghệ Thông Tin

Động lực học lập trình Java, Phần 8: Thay thế sự phản chiếu bằng việc tạo mã potx

22 245 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 22
Dung lượng 549,81 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Động lực học lập trình Java, Phần 8: Thay thế sự phản chiếu bằng việc tạo mã Tạo mã thời gian chạy cung cấp một cách để thay thế sự phản chiếu bằng sự truy cập trực tiếp nhằm đạt hiệu n

Trang 1

Động lực học lập trình Java, Phần 8: Thay thế sự phản chiếu bằng việc tạo

Tạo mã thời gian chạy cung cấp một cách để thay thế sự phản chiếu bằng sự truy cập trực tiếp nhằm đạt hiệu năng tối đa

Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc

Tóm tắt: Các phần trước trong loạt bài này, bạn đã tìm hiểu hiệu năng của sự

phản chiếu chậm hơn nhiều lần so với truy cập trực tiếp như thế nào và sau đó đã học về hoạt động lớp (classworking) với Javassist và Apache Byte Code

Engineering Library (BCEL-Thư viện kỹ thuật mã byte) Nhà tư vấn Java Dennis

Sosnoski hoàn thành loạt bài Động lực học lập trình Java của mình bằng cách giải

thích cách bạn có thể sử dụng hoạt động lớp trong thời gian chạy để thay thế mã phản chiếu bằng mã được tạo ra để lao hết tốc độ về phía trước

Bây giờ bạn đã thấy cách sử dụng các khung công tác Javassist và BCEL cho hoạt động lớp (xem liệt kê các bài viết trước trong loạt bài này), tôi sẽ cho bạn thấy một ứng dụng hoạt động lớp thực tế Ứng dụng này đang thay thế việc sử dụng sự phản chiếu bằng các lớp được tạo trong thời gian chạy và được nạp trực tiếp vào JVM Trong quá trình ráp nó lại với nhau, tôi sắp quay lại hai bài báo đầu tiên của loạt bài này cũng như trình bày Javassist và BCEL, vì thế nó tạo ra một sự kết thúc tốt đẹp cho những gì tạo thành một loạt các bài viết dài

Các mã phản chiếu theo hiệu năng

Quay lại Phần 2, tôi đã cho thấy cách mã phản chiếu chậm hơn nhiều lần so với

mã trực tiếp cho cả truy cập trường và cả các cuộc gọi phương thức Sự chậm chạp này không phải là một vấn đề cho nhiều ứng dụng, nhưng luôn có các trường hợp

ở đó hiệu năng rất quan trọng Trong những trường hợp này, mã phản chiếu có thể biểu diễn một nút cổ chai thực Mặc dù việc thay thế mã phản chiếu bằng mã được biên dịch tĩnh có thể rất lộn xộn và trong một số trường hợp (như trong các khung công tác ở đó các lớp hoặc các mục được mã phản chiếu truy cập được cung cấp trong thời gian chạy, chứ không phải là một phần của cùng một quá trình xây

dựng) thậm chí không thể thực hiện được nếu không cấu trúc lại toàn bộ ứng dụng

Hoạt động lớp cung cấp cho bạn một sự thay thế kết hợp hiệu năng của mã được biên dịch tĩnh với tính linh hoạt của mã phản chiếu Cách tiếp cận cơ bản ở đây là

để xây dựng một lớp tùy chỉnh trong thời gian chạy để bao bọc việc truy cập tới các lớp đích (trước đó đã đạt được bằng mã phản chiếu ) theo một cách mà mã mục đích chung của bạn có thể sử dụng Sau khi nạp các lớp tuỳ chỉnh vào JVM, rồi bạn thiết lập để chạy hết tốc độ

Trang 2

Thiết lập tầng

Liệt kê 1 đưa ra một điểm khởi đầu cho ứng dụng Ở đây tôi đã xác định một lớp bean đơn giản, HolderBean và một lớp truy cập, ReflectAccess Lớp truy cập có một đối số dòng lệnh Đối số này phải có tên của một trong các thuộc tính của lớp bean có giá trị int (value1 hoặc value2) Nó tăng giá trị của thuộc tính có tên, rồi in

ra cả hai giá trị thuộc tính trước khi thoát ra

Liệt kê 1.Phản chiếu một bean

public class HolderBean

{

private int m_value1;

private int m_value2;

Trang 3

public void setValue2(int value) {

public void run(String[] args) throws Exception {

if (args.length == 1 && args[0].length() > 0) {

// create property name

char lead = args[0].charAt(0);

String pname = Character.toUpperCase(lead) +

args[0].substring(1);

// look up the get and set methods

Method gmeth = HolderBean.class.getDeclaredMethod ("get" + pname, new Class[0]);

Method smeth = HolderBean.class.getDeclaredMethod ("set" + pname, new Class[] { int.class });

// increment value using reflection

HolderBean bean = new HolderBean();

Trang 4

Object start = gmeth.invoke(bean, null);

int incr = ((Integer)start).intValue() + 1;

smeth.invoke(bean, new Object[] {new Integer(incr)});

Dưới đây là một cặp ví dụ chạy ReflectAccess để minh họa cho các kết quả:

[dennis]$ java -cp ReflectAccess value1

Result values 1, 0

[dennis]$ java -cp ReflectAccess value2

Result values 0, 1

Trang 5

Đừng bỏ lỡ phần còn lại của loạt bài này

Phần 1, "Các lớp Java và nạp lớp" (04.2003)

Phần 2, "Giới thiệu sự phản chiếu" (06.2003)

Phần 3, "Ứng dụng sự phản chiếu" (07.2003)

Phần 4, "Chuyển đổi lớp bằng Javassist" (09.2003)

Phần 5, "Việc chuyển các lớp đang hoạt động" (02.2004)

Phần 6, "Các thay đổi hướng-khía cạnh với Javassist" (03.2004)

Phần 7, "Kỹ thuật bytecode với BCEL" (04.2004)

Xây dựng một lớp keo dán

Bây giờ tôi đã giải thích phiên bản phản chiếu của mã này, tôi sẽ cho bạn thấy cách để thay thế một lớp được tạo để sử dụng sự phản chiếu Có một vấn đề tế nhị liên quan đến việc làm cho sự thay thế này hoạt động đúng là quay lại chủ đề về hoạt động lớp trong Phần 1 của loạt bài này Vấn đề là tôi sẽ tạo một lớp trong thời gian chạy mà tôi muốn truy cập từ mã được biên dịch tĩnh của lớp truy cập, nhưng

do lớp được tạo không tồn tại với trình biên dịch, nên không có cách nào để tham chiếu nó trực tiếp

Vì vậy tôi có thể liên kết mã được biên dịch tĩnh với lớp đã tạo ra như thế nào? Giải pháp cơ bản là xác định một lớp hoặc giao diện cơ sở có thể được truy cập bằng mã được biên dịch tĩnh, sau đó mở rộng cho lớp cơ sở hoặc triển khai thực hiện giao diện đó trong lớp đã tạo ra Mã được biên dịch tĩnh sau đó có thể thực hiện cuộc gọi trực tiếp tới các phương thức, dù các phương thức này trên thực tế không được triển khai thực hiện trong thời gian chạy

Trong Liệt kê 2, tôi đã định nghĩa một giao diện, IAccess, nhằm cung cấp liên kết này cho mã đã tạo ra Giao diện này bao gồm ba phương thức Phương thức đầu tiên chỉ thiết lập một đối tượng đích được truy cập Hai phương thức khác là các

ủy quyền cho các phương thức get (nhận) và set (thiết lập) dùng để truy cập giá trị thuộc tính int

Trang 6

Liệt kê 2 Giao diện với lớp keo dán

public interface IAccess

{

public void setTarget(Object

target);

public int getValue();

public void setValue(int value);

Liệt kê 3.Thực hiện ví dụ lớp keo dán

public class AccessValue1 implements IAccess

Trang 7

sử dụng cách tiếp cận này như là một sự thay thế chung cho sự phản chiếu Hạn chế này không phải là một vấn đề miễn là bạn chỉ áp dụng kỹ thuật có chọn lọc trong các trường hợp ở đó hiệu năng phản chiếu thực sự là một nút cổ chai

Hỏi chuyên gia: Dennis Sosnoski về các vấn đề JVM và bytecode

Đối với các ý kiến hay các câu hỏi về tài liệu được trình bày trong loạt bài này, cũng như bất cứ điều gì khác có liên quan đến Java bytecode, định dạng lớp nhị phân Java hoặc các vấn đề JVM chung, hãy truy cập vào diễn đàn thảo luận JVM

và Bytecode, do Dennis Sosnoski kiểm soát

Tạo bằng Javassist

Tạo lớp thực hiện cho giao diện IAccess của Liệt kê 2 với Javassist thật dễ dàng Tôi chỉ cần tạo một lớp mới để triển khai thực hiện giao diện đó, thêm vào một biến thành viên cho tài liệu tham khảo đối tượng đích và kết thúc bằng cách thêm một hàm tạo không có đối số và các phương thức thực hiện đơn giản Liệt kê 4 sẽ

Trang 8

cho thấy đoạn mã Javassist để hoàn thành các bước này, được cấu trúc như là một cuộc gọi phương thức để lấy thông tin lớp đích và thông tin phương thức

nhận/thiết lập và trả về sự biểu diễn nhị phân của lớp được xây dựng:

Liệt kê 4 Xây dựng lớp keo dán Javassist

/** Parameter types for call with no parameters */

private static final CtClass[] NO_ARGS = {};

/** Parameter types for call with single int value */

private static final CtClass[] INT_ARGS = { CtClass.intType };

protected byte[] createAccess(Class tclas, Method gmeth,

Method smeth, String cname) throws Exception {

// build generator for the new class

String tname = tclas.getName();

ClassPool pool = ClassPool.getDefault();

CtClass clas = pool.makeClass(cname);

Trang 9

CtField field = new CtField(target, "m_target", clas);

clas.addField(field);

// add public default constructor method to class

CtConstructor cons = new CtConstructor(NO_ARGS, clas);

cons.setBody(";");

clas.addConstructor(cons);

// add public setTarget method

CtMethod meth = new CtMethod(CtClass.voidType, "setTarget",

new CtClass[] { pool.get("java.lang.Object") }, clas);

meth.setBody("m_target = (" + tclas.getName() + ")$1;");

clas.addMethod(meth);

// add public getValue method

meth = new CtMethod(CtClass.intType, "getValue", NO_ARGS, clas); meth.setBody("return m_target." + gmeth.getName() + "();");

clas.addMethod(meth);

// add public setValue method

meth = new CtMethod(CtClass.voidType, "setValue", INT_ARGS, clas); meth.setBody("m_target." + smeth.getName() + "($1);");

Trang 10

Tôi sẽ không duyệt qua đoạn mã này một cách chi tiết bởi vì, nếu bạn đang theo

dõi loạt bài này, thì hầu hết các hoạt động sẽ trông rất quen thuộc (và nếu bạn vẫn chưa theo kịp loạt bài này, ngay bây giờ hãy xem Phần 5 về một tổng quan làm

việc với Javassist)

Tạo bằng BCEL

Tạo lớp thực hiện cho giao diện IAccess của Liệt kê 2 bằng BCEL không phải khá

dễ dàng như với Javassist, nhưng nó vẫn không quá phức tạp Liệt kê 5 đưa ra đoạn mã cho mục đích này Mã này sử dụng cùng một chuỗi các hoạt động như

mã Javassist của Liệt kê 4, nhưng chạy hơi lâu hơn một chút vì cần phải giải thích

rõ ràng mỗi lệnh bytecode cho BCEL Như với phiên bản Javassist, tôi sẽ bỏ qua các chi tiết thực hiện (quay lại Phần 7 để có một tổng quan về BCEL nếu có bất cứ điều gì chưa rõ)

Liệt kê 5 Xây dựng lớp keo dán BCEL

/** Parameter types for call with single int value */

private static final Type[] INT_ARGS = { Type.INT };

/** Utility method for adding constructed method to class */

private static void addMethod(MethodGen mgen, ClassGen cgen) {

Trang 11

mgen.setMaxStack();

mgen.setMaxLocals();

InstructionList ilist = mgen.getInstructionList();

Method method = mgen.getMethod();

ilist.dispose();

cgen.addMethod(method);

}

protected byte[] createAccess(Class tclas,

java.lang.reflect.Method gmeth, java.lang.reflect.Method smeth, String cname) {

// build generators for the new class

String tname = tclas.getName();

ClassGen cgen = new ClassGen(cname, "java.lang.Object", cname + ".java", Constants.ACC_PUBLIC,

new String[] { "IAccess" });

InstructionFactory ifact = new InstructionFactory(cgen);

ConstantPoolGen pgen = cgen.getConstantPool();

// add target object field to class

FieldGen fgen = new FieldGen(Constants.ACC_PRIVATE,

Trang 12

new ObjectType(tname), "m_target", pgen);

cgen.addField(fgen.getField());

int findex = pgen.addFieldref(cname, "m_target",

Utility.getSignature(tname));

// create instruction list for default constructor

InstructionList ilist = new InstructionList();

ilist.append(InstructionConstants.ALOAD_0);

ilist.append(ifact.createInvoke("java.lang.Object", "<init>",

Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));

ilist.append(InstructionFactory.createReturn(Type.VOID));

// add public default constructor method to class

MethodGen mgen = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, null, "<init>", cname, ilist, pgen);

addMethod(mgen, cgen);

// create instruction list for setTarget method

ilist = new InstructionList();

ilist.append(InstructionConstants.ALOAD_0);

ilist.append(InstructionConstants.ALOAD_1);

ilist.append(new CHECKCAST(pgen.addClass(tname)));

Trang 13

ilist.append(new PUTFIELD(findex));

ilist.append(InstructionConstants.RETURN);

// add public setTarget method

mgen = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, new Type[] { Type.OBJECT }, null, "setTarget", cname, ilist, pgen);

addMethod(mgen, cgen);

// create instruction list for getValue method

ilist = new InstructionList();

// add public getValue method

mgen = new MethodGen(Constants.ACC_PUBLIC, Type.INT, Type.NO_ARGS, null, "getValue", cname, ilist, pgen);

addMethod(mgen, cgen);

Trang 14

// create instruction list for setValue method

ilist = new InstructionList();

// add public setValue method

mgen = new MethodGen(Constants.ACC_PUBLIC, Type.VOID,

INT_ARGS, null, "setValue", cname, ilist, pgen);

Trang 15

Bây giờ tôi đã có mã cho cả hai phiên bản Javassist và BCEL về xây dựng phương thức, tôi có thể tiến hành thử nghiệm chúng để xem chúng sẽ làm việc tốt như thế nào Vì lý do ban đầu của tôi về việc tạo mã trong thời gian chạy là để thay thế sự phản chiếu bằng một cái gì đó nhanh hơn, vì vậy sẽ thật tốt để có một sự so sánh hiệu năng để xem tôi đã thành công tốt như thế nào Chỉ cần chú ý đến nó, tôi cũng

sẽ xem xét thời gian cần thiết để xây dựng lớp keo dán với mỗi một trong các khung công tác

Liệt kê 6 thể hiện các phần chính của mã kiểm tra mà tôi sẽ sử dụng để kiểm tra hiệu năng Phương thức runReflection() chạy phần phản chiếu của thử nghiệm, runAccess() chạy phần truy cập trực tiếp và run() kiểm soát toàn bộ quá trình (bao gồm cả in ra các kết quả tính thời gian) Cả hai runReflection() và runAccess() lấy

số các vòng lặp được thực hiện làm một tham số, tham số này được chuyển lần lượt vào từ dòng lệnh (sử dụng mã không được hiển thị trong Liệt kê này, nhưng

có chứa trong phần tải về) Lớp DirectLoader (ở cuối Liệt kê 6) chỉ cung cấp một cách dễ dàng để nạp các lớp được tạo ra

Liệt kê 6 Mã thử nghiệm hiệu năng

/** Run timed loop using reflection for access to value */

private int runReflection(int num, Method gmeth, Method smeth,

Object obj) {

int value = 0;

try {

Object[] gargs = new Object[0];

Object[] sargs = new Object[1];

for (int i = 0; i < num; i++) {

// messy usage of Integer values required in loop

Trang 16

Object result = gmeth.invoke(obj, gargs);

Trang 17

}

public void run(String name, int count) throws Exception {

// get instance and access methods

HolderBean bean = new HolderBean();

String pname = name;

char lead = pname.charAt(0);

pname = Character.toUpperCase(lead) + pname.substring(1); Method gmeth = null;

Method smeth = null;

} catch (Exception ex) {

System.err.println("No methods found for property " + pname); ex.printStackTrace(System.err);

return;

}

Trang 18

// create the access class as a byte array

long base = System.currentTimeMillis();

String cname = "IAccess$impl_HolderBean_" + gmeth.getName() + "_" + smeth.getName();

byte[] bytes = createAccess(HolderBean.class, gmeth, smeth, cname);

// load and construct an instance of the class

Class clas = s_classLoader.load(cname, bytes);

IAccess access = null;

Ngày đăng: 07/08/2014, 10:22

HÌNH ẢNH LIÊN QUAN

Hình 1. Tốc độ mã phản chiếu so với tốc độ mã được tạo (thời gian tính bằng  mili giây) - Động lực học lập trình Java, Phần 8: Thay thế sự phản chiếu bằng việc tạo mã potx
Hình 1. Tốc độ mã phản chiếu so với tốc độ mã được tạo (thời gian tính bằng mili giây) (Trang 21)

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm