Các kiểu dữ liệu trong máy ảo Java Hầu hết các chỉ dẫn trong tập lệnh Java Virtual Machine đặt kiểu mã hóa thôngtin về các hoạt động sẽ được thực hiện.. Các lệnh truy cập tới các trường
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
Giáo viên hướng dẫn: TS Nguyễn Kim Khánh
HÀ NỘI 12-2017
Trang 2MỤC LỤC
I TỔNG QUAN VỀ MÁY ẢO JAVA 4
1 Khái niệm máy ảo Java 4
2 Quy trình thực thi 1 chương trình Java 4
3 Kiến trúc cơ bản của JVM 4
3.1 Thành phần tải lớp 5
3.2 Vùng dữ liệu thời gian chạy 6
3.3 Máy thực thi 7
II THÀNH PHẦN TẢI LỚP 8
1 Tổng quan về các lệnh chỉ dẫn của JVM 8
1.1 Các kiểu dữ liệu trong máy ảo Java 8
1.2 Lệnh load và store 9
1.3 Lệnh số học 10
1.4 Lệnh chuyển đổi kiểu dữ liệu 10
1.5 Tạo và thao tác với đối tượng 11
1.6 Lệnh quản lý trong operand stack 11
1.7 Lệnh điều khiển 11
1.8 Lệnh gọi và trả về của phương thức 11
1.9 Xử lý ngoại lệ 12
2 Cấu trúc dạng file class 12
2.1 Constant pool 15
2.2 Trường và phương thức: 17
2.3 Thuộc tính (Attribute) 18
3 Tải lớp, liên kết và khởi tạo (Loading, Linking and Initializing) 19
3.1 Tải lớp (Loading) 19
3.2 Liên kết (Linking) 20
3.3 Khởi tạo 21
III KHU VỰC DỮ LIỆU THỰC THI 23
1 Khu vực dữ liệu thực thi 23
1.1 Ngăn xếp JVM 23
Trang 31.2 Khu vực phương thức (Method Area) 23
1.3 Khu vực Heap (Heap Area) 23
1.4 Thanh ghi PC (PC Register) 23
1.5 Ngăn xếp phương thức native 23
2 Khung ngăn xếp 23
2.1 Các biến địa phương 24
2.2 Ngăn xếp toán hạng 25
IV MÁY THỰC THI 26
1 Bộ thông dịch bytecode 26
2 Bộ biên dịch JIT 27
2.1 Các cơ chế biên dịch trong JVM 27
2.2 Cơ chế hoạt động của bộ biên dịch JIT 28
3 Bộ dọn rác 31
3.1 Khái niệm tự động quản lý vùng nhớ (Automatic memory management) 31
3.2 Các kỹ thuật thu gom rác 31
TÀI LIỆU THAM KHẢO 34
Trang 4I TỔNG QUAN VỀ MÁY ẢO JAVA
1 Khái niệm máy ảo Java
Máy ảo Java, hay còn gọi là JVM là một máy tính toán trừu tượng cho phépmột máy tính có thể chạy được chương trình Java Có 3 khái niệm chính liên quan tớiJVM: đặc tả (specification), cài đặt (implementation) và thể hiện (instance)
Đặc tả là tài liệu mô tả chính thống về các thành phần cần thiết cho một cài đặtcủa JVM
Một cài đặt của JVM là một chương trình máy tính đáp ứng đầy đủ các yêu cầucủa đặc tả JVM
Một thể hiện của JVM là cài đặt của JVM được chạy trên 1 tiến trình cụ thể đểthực thi chương trình máy tính (đã được biên dịch sang Java bytecode)
2 Quy trình thực thi 1 chương trình Java
Chương trình Java sẽ được bộ biên dịch Java chuyển thành dạng file có đuôiclass File class thực chất là một dạng mã máy bao gồm mã Java bytecode và sẽ đượcthực thi bởi máy ảo Java Bởi vì Java bytecode không phụ thuộc vào nền tảng nào(platform independent) cho nên ứng dụng Java có thể chạy trên mọi nền tảng, miễn lànền tảng đó có máy ảo Java tương ứng
3 Kiến trúc cơ bản của JVM
Sơ đồ kiến trúc cơ bản của JVM:
Trang 5JVM được chia làm 3 thành phần chính:
Thành phần tải lớp (Class Loader Sub-system)
Vùng dữ liệu thời gian chạy (Runtime Data Area)
Máy thực thi (Execution Engine)
Trang 63.1.2 Liên kết (Linking)
Bước liên kết được thực hiện bao gồm:
Xác thực (Verify): Bộ xác thực bytecode sẽ kiểm tra nội dung các mã bytecodeđược sinh có đúng chuẩn hay không Trong trường hợp xác thực thất bại, ta sẽnhận được thông báo về lỗi xác thực
Chuẩn bị (Prepare): Tất cả các biến tĩnh (static variables) sẽ được khởi tạo vàgán giá trị mặc đinh
Phân giải (Resolve): Các tham chiếu vùng nhớ biểu tượng (symbolic memoryreference) được thay thế bởi các tham chiếu nguyên bản (original reference)trong khu vực phương thức (Method area)
3.1.3 Khởi tạo (Initialization)
Pha cuối của bước tải lớp, tại đây tất cả các biến tĩnh sẽ được gán giá trịnguyên bản, và các khối tĩnh (static block) sẽ được thực thi
3.2 Vùng dữ liệu thời gian chạy
Vùng dữ liệu thời gian chạy được chia làm 5 thành phần chính:
1 Khu vực phương thức: chứa tất cả các dữ liệu mức lớp, bao gồm cả các biếntĩnh Mỗi JVM chỉ có một khu vực phương thức, và khu vực này được coi là tàinguyên chia sẻ
2 Khu vực Heap: Tất cả các đối tượng và các thể hiện tương ứng của chúng vàmảng sẽ được lưu trữ tại đây Mỗi JVM chỉ có 1 khu vực Heap
3 Khu vực ngăn xếp: Với mỗi luồng, một ngăn xếp thời gian chạy riêng biệt sẽđược tạo ra Với mỗi lời gọi phương thức, một đăng ký sẽ được lưu trong bộnhớ ngăn xếp và được gọi là khung ngăn xếp (Stack Frame) Tất cả các biếncục bộ sẽ được lưu trong bộ nhớ ngăn xếp Khung ngăn xếp được chia làm 3thành phần:
a Mảng biến cục bộ: lưu giá trị các biến cục bộ liên quan tới phương thức
b Ngăn xếp toán hạng (Operand stack): nếu có mệnh lệnh nào cần thực thingay lập tức, ngăn xếp toán hạng sẽ hoạt động như không gian thời gianchạy (runtime workspace) để thực thi mệnh lệnh
c Dữ liệu khung (Frame data): lưu trữ các biểu tượng liên quan tới phươngthức Trong trường hợp xảy ra các ngoại lệ (exception), thông tin trongkhối Catch sẽ được duy trình trong thành phần này
4 Bộ đăng ký PC (PC Registers): Mỗi luồng sẽ có các bộ đăng ký PC riêng biệt
để lưu địa chỉ của mệnh lệnh đang thực thi Sau khi mệnh lệnh thực thi xong,
bộ đăng ký PC sẽ được cập nhật lệnh tiếp theo
5 Ngăn xếp phương thức Native (Native method stack): thành phần này chứa cácthông tin về phương thức native Với mỗi luồng, một ngăn xếp phương thứcnative riêng biệt sẽ được tạo ra
Trang 7 Bộ biên dịch JIT (JIT Compiler – Just In Time Compiler): Bộ biên dịch JIT giảiquyết nhược điểm của bộ thông dịch bằng cách biên dịch các mã bytecodethường được gọi nhiều lần sang ngôn ngữ máy để thực thi trực tiếp, giúp tănghiệu năng của hệ thống Các thành phần chính của bộ biên dịch JIT
o Bộ sinh mã trung gian (Intermediate Code Generator): sinh mã trunggian
o Bộ tối ưu mã (Code Optimizer): tối ưu mã trung gian sinh ở bước trên
o Bộ sinh mã mục tiêu (Target Code Generator): làm nhiệm vụ sinh mãnative hoặc mã máy
o Bộ lọc hồ sơ (Profiler): làm nhiệm vụ xác định các điểm nóng (hotspot):các phương thức thường xuyên được gọi tới
Bộ thu gom rác (Garbage Collector): thu thập và giải phóng các đối tượngkhông còn được tham chiếu nữa
Giao diện Java Native (Java Native Interface – JNI): làm nhiệm vụ tương tácvới các thư viện Native và cung cấp các thư viện native cần thiết cho Máy thực thi
Thư viện phương thức Native: là tập các thư viện native cần thiết cho Máy thựcthi
Trang 8Số lượng và kích thước của các toán hạng được xác định bởi opcode Nếu một toán
hạng có kích thước nhiều hơn một byte, thì nó được lưu trữ theo thứ tự bigendian
-byte cao xếp trước
Dòng lệnh bytecode chỉ nằm trong một byte Hai trường hợp ngoại lệ
là lookupswitch và tableswitch giới hạn toán hạng trong 4 byte.
1.1 Các kiểu dữ liệu trong máy ảo Java
Hầu hết các chỉ dẫn trong tập lệnh Java Virtual Machine đặt kiểu mã hóa thôngtin về các hoạt động sẽ được thực hiện Ví dụ, chỉ dẫn iload tải các nội dung của mộtbiến địa phương, mà phải là một số nguyên vào ngăn xếp chứa toán hạng Hai chỉ dẫn
có thể được cài đặt giống nhau, nhưng có các mã opcode riêng biệt (ví dụ: chỉ dẫn
iadd and fadd).
Đối với phần lớn các loại lệnh, kiểu dữ liệu trong câu lệnh được thể hiện rõ
ràng trong mnemonic opcode bởi một kí tự: I cho int, l cho long, s cho short, b cho byte, c cho char, f cho float, d cho double, và a cho reference
t
fcons t
dcons t
aconst
e
fstor e
dstor e
iaload laloa
d
faloa d
daloa d
caloa d
aaload
Tastore basto
re
sasto re
iastore lasto
re
fasto re
dasto re
casto re
aastore
Trang 9Tsub isub lsub fsub dsub
g
dcmp g if_Tcmp
OP
if_icmp OP
if_acmp OP Treturn ireturn lretu
rn
fretu rn
dretu rn
areturn
Kiểu dữ liệu trong tập lệnh máy ảo JavaBảng trên tóm tắt các kiểu dữ liệu hỗ trợ trong tập lệnh của Java Virtualmachine Đối với một lệnh cụ thể, thông tin kiểu dữ liệu được xây dựng bằng việc
thay thế T trong cột opcode bằng ký tự trong cột kiểu dữ liệu, nếu kiêu dữ liệu ở cột
nào trống thì có nghĩa không tồn tại lệnh hỗ trợ kiểu dữ liệu đó
Trang 10 Lệnh store chuyển giá trị từ operand stack vào local variables: istore,istore_<n>, lstore, lstore_<n>, fstore, fstore_<n>, dstore, dstore_<n>, astore,astore_<n>.
Lệnh load 1 hằng số vào trong operand stack: bipush, sipush, ldc, ldc_w,ldc2_w, aconst_null, iconst_m1, iconst_<i>, lconst_<l>, fconst_<f>,dconst_<d>
Các lệnh truy cập tới các trường của đối tượng hoặc các phần tử của mảng cũngđược sử dụng để chuyển giá trị từ đối tượng hoặc phần tử đó vào trong operand stack
1.3 Lệnh số học
Lệnh số học được sử dụng để tính toán 2 giá trị trên được lưu trữ trong operandstack Có 2 loại chính trong lệnh số học đó là việc tính toán trên các giá trị integer vàfloat-point Các lệnh số học được liệt kê ở dưới đây:
Cộng: iadd, ladd, fadd, dadd
Trừ: isub, lsub, fsub, dsub
Nhân: imul, lmul, fmul, dmul
Chia: idiv, ldiv, fdiv, ddiv
Chia lấy dư: irem, lrem, frem, drem
Phủ định: ineg, lneg, fneg, dneg
Dịch bit: ishl, ishr, iushr, lshl, lshr, lushr
OR các bit: ior, lor
AND các bit: iand, land
XOR các bit: ixor, lxor
So sánh: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
1.4 Lệnh chuyển đổi kiểu dữ liệu
Lệnh chuyển đổi kiểu dữ liệu cho phép chuyển đổi các kiểu dữ liệu số họctrong Java Virtual Machine JVM hỗ trợ trực tiếp việc chuyển đồi từ kiểu dữ liệu nhỏhơn sang kiểu dữ liệu lớn hơn:
int chuyển đổi thành long, float, hoặc double
long chuyển đổi thành float or double
float chuyển đổi thành double
Lệnh chuyển đổi sang kiểu dữ liệu lớn hơn có dạng i2l, i2f, i2d, l2f, l2d, và f2d Việc chuyển đổi từ kiểu int sang kiểu long hoặc kiểu int sang kiểu double sẽ không
làm mất mát thông tin của giá trị ứng với kiểu dữ liệu trước đó Tuy nhiên việc chuyểnđổi từ kiểu float sang kiểu double thì có thể gây mất mát thông tin của giá trị
Ngoài ra JVM cũng hỗ trợ việc chuyển đổi kiểu dữ liệu về kiểu dữ liệu nhỏ hơn:
int chuyển đổi về byte, short, hoặc char
long chuyển đổi về int
float chuyển đổi về int hoặc long
Trang 11 double chuyển đổi về int, long, hoặc float
Lệnh chuyển đổi về kiểu dữ liệu nhỏ hơn có dạng i2b, i2c, i2s, l2i, f2i, f2l, d2i, d2l, and d2f Việc chuyển đối sang kiểu dữ liệu nhỏ hơn này sẽ làm mất đi độ chính
xác
1.5 Tạo và thao tác với đối tượng
Mặc dù thể hiện của lớp và mảng là những đối tượng, tuy nhiên trong JVMviệc tạo và sử dụng chúng không giống nhau:
Tạo mới thể hiện của 1 lớp ta sử dụng: new
Tạo mới một mảng ta sử dụng: newarray, anewarray, multianewarray
Truy cập tới các thuộc tính của đối tượng ta sử dụng: getfield, putfield, getstatic, putstatic.
Nạp các phần tử của mảng vào trong operand stack sử dụng: baload, caload, saload, iaload, laload, faload, daload, aaload
Lưu mỗi giá trị của operand stack vào mỗi phần tử của mảng sử dụng: bastore, castore, sastore, iastore, lastore, fastore, dastore, aastore
Tính độ dài của mảng thông qua: arraylength
1.6 Lệnh quản lý trong operand stack
Số lượng tập lệnh sử dụng trực tiếp trong operand stack bao gồm: pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2, swap.
Một số lệnh điều khiển không điều kiện: goto, goto_w, jsr, jsr_w, ret.
Lệnh điều kiện kết hợp: tableswitch, lookupswitch
1.8 Lệnh gọi và trả về của phương thức
Có 5 loại lệnh để gọi phương thức:
invokevirtual: gọi phương thức của một đối tượng.
invokeinterface: gọi phương thức của 1 interface, tìm kiếm phương thức được
cài đặt bởi 1 đối tượng thời gian chạy (run-time object)
invokespecial: gọi phương thức cần có xử lý đặc biệt: phương thức khởi tạo của một đối tượng, phương thức dạng private, hoặc phương thức của lớp cha.
invokestatic: gọi phương thức static của lớp.
invokedynamic:gọi phương thức là mục tiêu của đối tượng gọi gắn với chỉ dẫn invokedynamic
Trang 12Lệnh trả về của phương thức có các kiểu: ireturn (sử dụng để trả về giá trị kiểu boolean, byte, char, short, hoặc int), lreturn, freturn, dreturn, và areturn.
1.9 Xử lý ngoại lệ
Ngoại lệ trong JVM được xử lý thông qua lệnh athrow Các ngoại lệ cũng có
thể được phát ra bởi các chỉ dẫn của JVM nếu có xảy ra các điều kiện bất thường
2 Cấu trúc dạng file class
Như đã trình bày ở trên, file class là kết quả biên dịch từ file java Mỗi file sẽchứa định nghĩa của 1 lớp hoặc 1 interface Một file class là một chuỗi các byte 8 bit,tất cả các thành phần 16 bit, 32 bit và 64 bit đều được dựng lại bằng việc đọc 2, 4 và 8byte liên tiếp Tất cả các thành phần dữ liệu là đa byte (multibyte) đều sẽ được lưu trữtheo thứ tự big endian, tức là byte cao xếp trước
Cấu trúc của 1 file class có thể được mô tả như sau:
Các thành phần trong file class:
magic: số đặc trưng cho định dạng file class với giá trị 0xCAFEBABE
minor_version, major_version: số hiệu phiên bản (số nhỏ, số lớn) củađịnh dạng Định dạng file class cho Java version 7 là 0x0000, 0x0033 (51.00)
constant_pool_count: có giá trị bằng số lượng phần tử trong bảng
constant_pool cộng với 1 Chỉ số của 1 phần tử trong constant_pool làhợp lệ nếu lớn hơn 0 và nhỏ hơn constant_pool_count
constant_pool[]: là một bảng cấu trúc đại diện cho các hằng số xuất hiệntrong cấu trúc của file class bao gồm chuỗi, tên lớp, tên interface, tên trường…
Trang 13 access_flags: có giá trị bằng hợp của các cờ đánh dấu quyền truy cập cũngnhư đặc tính của lớp hay interface của file class Các cờ này bao gồm:
ACC_PUBLIC 0x0001 Khai báo public: có thể truy nhập từ
ngoài package
ACC_FINAL 0x0010 Khai báo final: không cho kế thừa
ACC_SUPER 0x0020
ACC_INTERFACE 0x0200 Đánh dấu là interface, không phải lớp
ACC_ABSTRACT 0x0400 Khai báo abstract: không được khởi
o Với interface, giá trị của phần tử này là chỉ số trỏ tới phần tử có cấu trúc
CONSTANT_Class_info đại diện cho lớp Object
interfaces_count: giá trị bằng số lượng interface mà lớp/interface này kếthừa
interfaces[]: một mảng các giá trị là chỉ số trỏ tới bảng constant_pool
mà phần tử tại đó có cấu trúc CONSTANT_Class_info đại diện cho interfaceđược lớp/interface này kế thừa trực tiếp
fields_count: có giá trị bằng số phần tử trong bảng fields
fields[]: mỗi phần tử trong bảng fields phải có cấu trúc field_info
đưa ra mô tả đầy đủ cho một trường được khai báo trong lớp/interface này.Bảng fields không bao gồm các trường kế thừa từ lớp cha hoặc interface cha
methods_count: có giá trị bằng số phần tử trong bảng methods
methos[]: mỗi phần tử trong bảng methods phải có cấu trúc method_info
đưa ra mô tả đầy đủ về một phương thức trong lớp/interface này Nếu các cờ
ACC_NATIVE hoặc ACC_ABSTRACT không được thiết lập, các chỉ dẫn JVM cài
Trang 14đặt cho phương thức sẽ được cung cấp Bảng method không bao gồm cácphương thức kế thừa từ lớp cha hoặc interface cha.
attributes_count: có giá trị bằng số phần tử trong bảng attributes
attributes[]: mỗi phần tử trong bảng attributes phải có cấu trúc
attribute_info Các phần tử này chứa thông tin của trường, phương thức
và lớp
Sơ đồ mô tả cấu trúc file class:
Trang 152.1 Constant pool
Constant pool là thành phần quan trọng nhất trong file class, chứa các thông tin
sẽ được trỏ tới bởi các thành phần khác trong file Constant pool là một mảng cácphần tử với chỉ số bắt đầu bằng 1 Mỗi phần tử bắt đầu bằng 1 nhãn 1 byte đánh dấuloại phần tử:
Trang 16 CONSTANT_Utf8: đại diện cho các chuỗi UTF8.
CONSTANT_Integer: đại diện cho giá trị số nguyên, độ dài 4 byte
CONSTANT_Float: đại diện cho giá trị float tuân theo định dạng dấu phẩyđộng IEEE 754, độ dài 4 byte
CONSTANT_Long: đại diện cho giá trị long, độ dài 8 byte Lưu ý phần tử nàyđược đếm 2 lần trong số lượng phần tử của constant pool
CONSTANT_Double: đại diện cho giá trị double, độ dài 8 byte Tương tự
CONSTANT_Long, phần tử này cũng được đếm 2 lần trong số lượng phần tửcủa constant pool
CONSTANT_Class: đại diện cho một lớp hoặc interface, bao gồm 1 chỉ sốtrong constant pool trỏ tới 1 giá trị CONSTANT_Utf8 chứa tên củalớp/interface Ví dụ: java/lang/Thread
CONSTANT_String: đại diện cho các chuỗi hằng trong lớp, bao gồm 1 chỉ sốtrong constant_pool trỏ tới 1 giá trị trong CONSTANT_Utf8 chứa giá trị củachuỗi
CONSTANT_Fieldref: tham chiếu cho 1 trường, bao gồm 1 chỉ số trỏ tới
CONSTANT_Class mà trường này nằm trong và 1 chỉ số trỏ tới
CONSTANT_NameAndType đại diện cho tên và loại của trường
CONSTANT_Methodref: tham chiếu cho 1 phương thức, bao gồm 1 chỉ số trỏtới CONSTANT_Class mà phương thức này nằm trong và 1 chỉ số trỏ tới
CONSTANT_NameAndType đại diện cho tên và mô tả của phương thức (methoddescriptor) Lưu ý CONSTANT_Class phải đại diện cho 1 lớp chứ không phảiinterface
CONSTANT_InterfaceMethodref: tương tự CONSTANT_Methodref nhưngphần tử CONSTANT_Class đại diện cho 1 interface chứ không phải lớp
CONSTANT_NameAndType: đại diện cho thông tin 1 trường hoặc 1 phươngthức, bao gồm 2 chỉ số trong bảng constant pool trỏ tới giá trị trong
CONSTANT_Utf8, chỉ số thứ nhất là tên trường/phương thức, chỉ số thứ 2 làkiểu trường/mô tả của phương thức
CONSTANT_MethodHandle: phần tử này được dùng để phân giải tham chiếubiểu tượng tới 1 xử lý phương thức (method handle), bao gồm 1 byte chỉ thịloại xử lý phương thức (có giá trị từ 1 tới 9) và 2 byte là chỉ số trong bảngconstant pool trỏ tới CONSTANT_Fieldref hoặc CONSTANT_Methodref
hoặc CONSTANT_InterfaceMethodref tùy thuộc vào loại xử lý phươngthức
CONSTANT_MethodType: trường này để phân giải kiểu của phương thức, baogồm 1 chỉ số trong bảng constant pool trỏ tới CONSTANT_Utf8 mô tả kiểuphương thức
Trang 17 CONSTANT_InvokeDynamic: phần tử này được chỉ dẫn invokedynamic sửdụng để xác định một phương thức bootstrap, bao gồm 1 chỉ số của bảngphương thức bootstrap và 1 chỉ số của bảng constant pool trỏ tới
CONSTANT_NameAndType xác định tên phương thức và mô tả của phươngthức
Ta có thể tóm tăt cấu trúc các loại phần tử của bảng constant pool qua sơ đồdưới đây:
Chú ý: Các loại xử lý phương thức được liệt kê trong bảng sau:
Loại xử lý phương thức (Method handle kind)