tìm hiểu về jvm
DESCRIPTION
Tìm hiểu về jvmTRANSCRIPT
TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN ĐIỆN TỬ - VIỄN THÔNG
.........****.........
BÁO CÁO BÀI TẬP LỚN
HỆ ĐIỀU HÀNH
Đề tài: Tìm hiểu về JVM (Java Virtual Machine)
Hà Nội 5/2016
LỜI GIỚI THIỆU
Tìm hiểu JVM
Java là một ngôn ngữ lập trình hướng đối tượng (OOP) và dựa trên các lớp
(class). Khác với phần lớn ngôn ngữ lập trình thông thường, thay vì biên dịch mã
nguồn thành mã máy hoặc thông dịch mã nguồn khi chạy, Java được thiết kế để biên
dịch mã nguồn thành bytecode, bytecode sau đó sẽ được môi trường thực thi (runtime
environment) chạy.
Java được tạo ra với tiêu chí "Viết (code) một lần, thực thi khắp nơi" ("Write
Once, Run Anywhere" (WORA)). Chương trình phần mềm viết bằng Java có thể chạy
trên mọi nền tảng (platform) khác nhau thông qua một môi trường thực thi với điều
kiện có môi trường thực thi thích hợp hỗ trợ nền tảng đó. Môi trường thực thi của Sun
Microsystems hiện hỗ trợ Sun Solaris, Linux, Mac OS,FreeBSD & Windows. Ngoài
ra, một số công ty, tổ chức cũng như cá nhân khác cũng phát triển môi trường thực thi
Java cho những hệ điều hành khác như BEA, IBM, HP.... Trong đó đáng nói đến nhất
là IBM Java Platform hỗ trợ Windows, Linux, AIX & z/OS.
Java Virtual Machine (JVM) bản chất là một chương trình có thể thực thi các
đoạn mã lập trình của Java, và đặc điểm của những chương trình viết bằng Java là đều
có thể chạy trên bất kỳ môi trường nào, miễn là có cài máy ảo JVM trên đó. Việc này
sẽ tạo một động lực rất lớn thúc đẩy việc dùng lại các phần mềm Java đã được viết
trước đây và việc phát triển phần mềm trên iPhone và iTouch.
Vai trò to lớn, tính thực dụng và các ứng dụng rộng rãi của JVM đã nhận được
sự quan tâm sâu sắc của các kỹ sư, chuyên viên lập trình cũng như những người đam
mê công nghệ. Song việc tìm hiểu và tiếp cận JVM gặp nhều khó khăn do chưa có
nhiều tài liệu mô tả một cách chi tiết và rõ ràng. Cũng như thực tế có rất ít các trường
đại học dạy về vấn đề này. Do đó qua tìm hiểu kết hợp sự hướng dẫn cũng như giúp đỡ
của thầy Vũ Song Tùng, chúng em đã đưa ra một bản báo cáo một cách cơ bản về
JVM. Hi vọng tài liệu này đóng góp một phần kiến thức cơ bản về JVM cho những ai
muốn tìm hiểu. bản báo cáo này vẫn còn nhiều thiếu sót, chúng em mong có được sự
đóng góp ý kiến cũng như phản biện của người đọc để hoàn thiện hơn
Xin chân thành cảm ơn !
MỤC LỤC
Page | 2
Tìm hiểu JVM
LỜI GIỚI THIỆU..............................................................................................................2
I. Giới thiệu chung về Java Virtual Machine (JVM).............................................................5
1.1. Giới thiệu chung..........................................................................................................................5
1.1.1. Giới thiệu Java Virtual Machine (JVM).........................................................5
1.1.2. Máy ảo Java là gì?.............................................................................................5
1.1.3. Kiến trúc của máy ảo Java................................................................................6
1.2. Phương thức thực thi.................................................................................................................8
1.2.1. Quản lý bộ nhớ và dọn rác................................................................................8
1.2.2. Quá trình kiểm tra file .class............................................................................8
II. Tìm hiểu chi tiết về các thành phần trong Java Virtual Machine(JVM)................9
2.1. Tìm hiểu về Java Memory...................................................................................9
2.3. Các Kiểu Dữ Liệu...............................................................................................30
2.4. Word Size............................................................................................................33
2.5. Program Counter................................................................................................33
2.6. Threads (luồng trong Java)...............................................................................34
2.7. Native Method Interface....................................................................................36
PHỤ LỤC............................................................................................................................4
DANH SÁCH HÌNH VẼ...................................................................................................................4
DANH SÁCH BẢNG.........................................................................................................................4
TÀI LIỆU THAM KHẢO...............................................................................................38
PHỤ LỤC
DANH SÁCH HÌNH VẼ
Hình 1.1. Sơ đồ đơn giản về máy ảo Java………………………………….….…......7Page | 3
Tìm hiểu JVM
Hình 1.2. Sơ đồ bên trong của JVM………………..………………..……….…..…..7
Hình 2.1. Khu vực chia sẻ dữ liệu các thành phần…………...……..…….….……10
Hình 2.2. Hoạt động Stack trong JVM……………………..………….…….….….11
Hình 2.3. Mô tả một cách tiếp cận đối tượng của Heap…………………….…..…14
Hình 2.4. Một cách tiếp cận Object khác của Heap……….………………..….….15
Hình 2.5. Một cách tham chiếu đối tượng………………………….………..….….16
Hình 2.6. Đại diện của mảng trên Heap…………………………….………..….…17
Hình 2.7. Tham số phương thức trên phần biến địa phương của Java Stack..…20
Hình 2.8. Mô phỏng việc thêm 2 biến cục bộ……………………………….……...22
Hình 2.9. Mô phỏng hoạt động stack qua Ví Dụ………………………….….........23
Hình 2.10. Phân bố các khung hình từ ngăn xếp liền kề nhau…………….…...…24
Hình 2.11. Stack cho một thread khi Java gọi phương thức địa phương………...26
Hình 2.12. Các kiểu dữ liệu trong Java………………………………….….…...…31
DANH SÁCH BẢNG
Bảng 2.1. Phạm vi các kiểu dữ liệu trong máy ảo Java………………………...….33
Page | 4
Tìm hiểu JVM
I. Giới thiệu chung về Java Virtual Machine (JVM)
1.1. Giới thiệu chung
1.1.1. Giới thiệu Java Virtual Machine (JVM)
Tất cả các chương trình muốn thực thi được thì phải được biên dịch ra mã máy.
Mã máy của từng kiến trúc CPU của mỗi máy tính là khác nhau (tập lệnh mã máy của
CPU Intel, CPU Solarix, CPU Macintosh … là khác nhau), vì vậy trước đây 1 chương
trình sau khi được biên dịch xong chỉ có thể chạy trên 1 kiến trúc CPU cụ thể nào đó.
Đối với Intel chúng ta có thể chạy các hệ điều hành như Microsoft Windows, Unix,
Linux…
Chương trình thực thi được trên Windows được biên dịch dưới dạng file có
đuôi .EXE còn trên Linux thì được biên dịch dưới dạng file có đuôi .ELF, vì vậy trước
đây 1 chương trình chạy trên Windows muốn chạy trên các hệ điều hành khác như
Linux phải chỉnh sửa biên và biên dịch lại.
Ngôn ngữ lập trình Java ra đời, nhờ vào máy ảo Java mà khó khăn trên được
khắc phục. Một chương trình được viết bằng ngôn ngữ lập trình Java sẽ được biên dịch
ra mã của máy ảo Java (mã Java bytecode). Sau đó máy ảo Java chịu trách nhiệm
chuyển mã Java bytecode thành mã máy tương ứng. Sun Microsystem chịu trách
nhiệm phát hiện các máy ảo Java chạy trên các hệ điều hành trên các kiến trúc CPU
khác nhau.
Máy ảo java được sinh ra với 3 mục đích chính:
Dịch mã java ra mã máy chạy được trên các hệ điều hành khác nhau.
Tăng tốc độ.
Nâng cao độ bảo mật và tránh virus phá source code.
1.1.2. Máy ảo Java là gì?
Như đã biết, JVM bản chất là một chương trình có thể thực thi các đoạn mã lập
trình của Java, và đặc điểm của những chương trình viết bằng Java là đều có thể chạy
trên bất kỳ môi trường nào, miễn là có cài máy ảo JVM trên đó. Việc này sẽ tạo một
Page | 5
Tìm hiểu JVM
động lực rất lớn thúc đẩy việc dùng lại các phần mềm Java đã được viết trước đây và
việc phát triển phần mềm trên iPhone và iTouch.
Máy ảo là một phần mềm dựa trên cơ sở máy tính ảo. Nó có tập hợp các lệnh
logic để xác định các hoạt động của máy tính.Người ta có thể xem nó như một hệ điều
hành thu nhỏ. Nó thiết lập các lớp trừu tượng cho: Phần cứng bên dưới,hệ điều
hành,mã đã biên dịch.
Trình biên dịch chuyển mã nguồn thành tập các lệnh của máy ảo mà không phụ
thuộc vào phần cứng cụ thể. Trình thông dịch trên mỗi máy sẽ chuyển tập lệnh này
thành chương trình thực thi. Máy ảo tạo ra một môi trường bên trong để thực thi các
lệnh bằng cách:
Nạp các file.class
Quản lý bộ nhớ
Dọn "rác"
Việc không nhất quán của phần cứng làm cho máy ảo phải sử dụng ngăn xếp
để lưu trữ các thông tin sau:
Các "Frame" chứa các trạng thái của các phương pháp.
Các toán hạng của mã bytecode
Các tham số truyền cho phương pháp
Các biến cục bộ
Khi JVM thực thi mã, một thanh ghi cục bộ có tên "Program Counter" được sủ
dụng. Thanh ghi này trỏ tới lệnh đang thực hiện. Khi cần thiết, có thể thay đổi nội
dung thanh ghi để đổi hướng thực thi của chương trình. Trong trường hợp thông
thường thì từng lệnh một nối tiếp nhau sẽ được thực thi.
Một khái niệm thông dụng khác trong Java là trình biên dịch "Just In Time-
JIT". Các trình duyệt thông dụng như Netscape hay IE đều có JIT bên trong để tăng
tốc độ thực thi chương trình Java. Mục đích chính của JIT là chuyển tập lệnh bytecode
thành mã máy cụ thể cho từng loại CPU. Các lệnh này sẽ được lưu trữ và sử dụng mỗi
khi gọi đến.
1.1.3. Kiến trúc của máy ảo Java
Hình 1.1 là sơ đồ đơn giản về máy ảo Java
Page | 6
Tìm hiểu JVM
Hình 1.1 Sơ đồ đơn giản về máy ảo Java
Đầu tiên sẽ là biên dịch file.java sang file.class và được máy ảo Java chuyển hoá thành
các mã máy tương ứng với các hệ điều hành tương ứng.
Để tìm hiểu sâu hơn chúng ta đi vào sơ đồ nội bộ của 1 máy ảo Java
Hình 1.2 Sơ đồ bên trong của Java Virtual Machine
Như trong hình ta thấy Java có 3 thành phần chính
1. Class Loader: Tìm kiếm và nạp các file.class vào vùng nhớ java
Page | 7
Tìm hiểu JVM
2. Data Area: Vùng nhớ hệ thống cấp phát cho Java Virtual Machine
3. Execution Engine: chuyển các lệnh của JVM trong file.class thành
mã máy tương ứng với các hệ điều hành
1.2. Phương thức thực thi
1.2.1. Quản lý bộ nhớ và dọn rác
Trong C, C++ hay Pascal người lập trình sử dụng phương pháp nguyên thủy để
cấp phát và thu hồi bộ nhớ ở vùng " Heap". Heap là vùng bộ nhớ lớn được phân chia
cho tất cả các Thread.
Để quản lý Heap, bộ nhớ được theo dõi qua các danh sách sau:
- Danh sách các vùng nhớ rảnh chưa cấp phát.
- Danh sách các vùng đã cấp
Khi có một yêu cầu về cấp phát bộ nhớ, hệ thống kiểm tra xem xét trong danh
sách chưa cấp phát để lấy ra khối bộ nhớ đầu tiên có kích cỡ sát nhất. Chiến thuật cấp
phát này giảm tối thiểu việc phân mảnh của Heap. "Coalescing" là kỹ thuật khác cũng
giảm thiểu việc phân mảnh của Heap bằng cách gom lại các vùng nhớ chưa dùng liền
nhau. Còn kỹ thuật sắp xếp lại các phần đã dùng để tạo vùng nhớ lớn hơn gọi là
"Compaction" Java sử dụng hai Heap riêng biệt cho cấp phát vùng nhớ tĩnh và vùng
nhớ động. Một Heap(Heap tĩnh) chứa các định nghĩa về lớp, các hằng và danh sách
các phương pháp. Heap còn lại(Heap động) được chia làm hai phần được cấp phát theo
hai chiều ngược nhau. Một bên chứa đối tượng còn bên kia chứa con trỏ trỏ đến đối
tượng đó.
"Handle" là cấu trúc bao gồm hai con trỏ. Một trỏ đến bảng phương pháp của đối
tượng, con trỏ thứ hai trỏ đến chính đối tượng đó. Chú ý rằng khi "compaction" cần
cập nhập lại giá trị con trỏ của cấu trúc" Handle". Thuật toán dọn rác có thể áp dụng
cho các đối tượng đặt trong Heap động. Khi có yêu cầu về bộ nhớ, trình quản lý Heap
trước tiên kiểm tra danh sách bộ nhớ chưa cấp phát. Nếu không tìm thấy khối bộ nhớ
nào phù hợp(về kích cỡ) thì trình dọn rác sẽ được kịch hoạt khi hệ thống rảnh. Nhưng
khi đòi hỏi bộ nhớ cấp bách thì trình dọn rác sẽ được kích hoạt ngay.
Trình dọn rác gọi hàm Finalize trước khi dọn dẹp đối tượng. Hàm này sẽ dọn
dẹp các tài nguyên bên ngoài như các file đang mở. Công việc này không được trình
dọn rác thực thi.Page | 8
Tìm hiểu JVM
1.2.2. Quá trình kiểm tra file .class
Việc kiểm tra được áp dụng cho tất cả các file .class sắp được nạp lên bộ nhớ để
đảm bảo tính an toàn.
Trình "Class Loader" sẽ kiểm tra tất cả các file .class không phụ thuộc hệ điều
hành với mục đích giám sát sự tuân thủ các nghi thức để phát hiện các file ,class có
nguy cơ gây hư hỏng đến bộ nhớ, hệ thống file cục bộ, mạng hoặc hệ điều hành. Quá
trình kiểm tra sẽ xem xét đến tính toàn vẹn toàn cục của lớp.
File .class bao gồm ba phần logic là:
1. Byecode
2. Thông tin về Class như phương pháp, giao diện và các giá trị được tập
hợp trong quá trình biên dịch.
3. Các thuộc tính về lớp.
Các thông tin của file . class được xem xét riêng rẽ trong các bảng như sau:
Bảng file chứa các thuộc tính
Bảng Method chứa các hàm của class
Bảng Interface chứa các giao diện và các hằng số
Quá trình kiểm tra file .class được thực hiện ở bốn mức:
• Mức đầu tiên thực hiện việc kiểm tra cú pháp để đảm bảo tính cấu trúc và
tính toàn vẹn cú pháp của file .class được nạp.
• Mức thứ hai sẽ xem xét file.class để đảm bảo các file này không vi phạm
các nguyên tắc về sự nhất quán ngữ nghĩa.
• Mức thứ ba sẽ kiểm tra bytecode. Trong bước này các thông tin so sánh sẽ
là số thông số truyền của hàm, khả năng truy xuất sai chỉ số của mảng, chuỗi, biểu
thức.
• Mức thứ tư sẽ kiểm tra trong thời gian thực thi để giám sát các việc còn
lại mà ba bước trên chưa làm. Ví dụ như liên kết tới các lớp khác trong khi thực thi,
hay kiểm tra quyền truy xuất. Nếu mọi điều thỏa mãn, lớp sẽ được khởi tạo.
II. Tìm hiểu chi tiết về các thành phần trong Java Virtual Machine (JVM)
2.1. Tìm hiểu về Java Memory
Mỗi khi máy ảo java (JVM) chạy một chương trình, nó cần bộ nhớ để lưu trữ
nhiều thứ bao gồm cả byte code và các thông tin nó lấy từ các file nạp, đối tượng
Page | 9
Tìm hiểu JVM
chương trình được khởi tạo, các tham số, các giá trị trả lại, các biến địa phương và các
kết quả trung gian trong quá trình tính toán. Các máy ảo Java nó tổ chức bộ nhớ thành
nhiều vùng dữ liệu theo thời gian.
Mặc dù khu vực vùng dữ liệu đó tồn tại trong một số hình thức của máy ảo
java, đặc điểm của chúng là khá trừu tượng. Mỗi vùng nhớ có một cách tổ chức khác
nhau giúp nhà thiết kế lựa chọn để triển khai công việc.
Một số vùng nhớ được truy cập được từ mọi thành phần nhưng có những vùng
nhớ thì không. Mỗi khi JVM nạp một file class nó sẽ phân tích thông tin về các loại dữ
liệu nhị phân chứa trong file class đó. Nó sẽ đặt các thông tin này vào 1 vùng nhớ
method area. Khi máy ảo hoạt động tất cả các đối tượng (object) sẽ được lưu vào vùng
nhớ Heap.
Hình 2.1 Khu vực chia sẻ dữ liệu các thành phần
Mỗi thread xuất hiện nó được ghi vào 1 thanh ghi pc(chương trình đếm) và Java
Stack. Nếu thread đang thực thi một phương thức của Java (không phải là phương thức
địa phương) giá trị của thanh ghi pc chỉ lệnh kế tiếp được thực thi. Mỗi thread Java
stack được lưu trữ trạng thái của Java (không có nguồn gốc) phương pháp gọi cho
Java. Trạng thái của phương pháp gọi Java bao gồm: các biến tại địa phương, các biến
của nó được dẫn, các giá trị trả về (nếu có) và các giá trị trung gian trong quá trình
thực hiện. Các trạng thái của phương thức gọi được lưu trữ phụ thuộc vào stack địa
phương, hoặc các khu vực bộ nhớ thực hiện các phụ thuộc khác.
Page | 10
Tìm hiểu JVM
Các Java Stack bao gồm các khung stack (khung). Một khung Stack chứa các
trạng thái của một chương trình gọi Java. Khi có một thread gọi một phương pháp nào
đó các máy ảo đẩy khung mới của chủ đề đó lên Java Stack. Khi chương trình này
được chạy xong máy ảo tiến hành loại bỏ khung của phương thức đó.
Các máy ảo Java không có các thanh ghi để lưu các giá trị trung gian. Các tập
lệnh sử dụng chính Stack để lưu các giá trị trung gian. Cách làm này được thực hiện
bởi các nhà thiết kế Java nhằm làm cho máy ảo Java được nhỏ gọn nhất để tạo điều
kiện có thể cài đặt sử dụng trên các kiến trúc nhỏ. Ngoài ra các kiến trúc stack dựa trên
các tập lệnh của máy ảo java tạo điều kiện làm việc tối ưu hoá thực hiện bằng cách
just-in-time và trình biên dịch hoạt động ở thời gian chạy trong khi thực thi máy ảo.
Hình 2.2 Hoạt động Stack trong JVM
Hình 2.2 là một hình ảnh thể hiện một trường hợp của máy ảo có 3 thread được
thực hiện trong đó có một thread 3 là phương thức địa phương. Khung stack cho
phương thức thực thi được hiện thi trong màu sáng. Đối với một Thread đang được
thực hiện một phương thức của Java , thanh ghi Pc chỉ lệnh kế tiếp được thực thi.
Page | 11
Tìm hiểu JVM
Trong hình 2.2 cũng như vậy thanh ghi pc được thể hiện trong một màu sáng. Bởi về
thread 3 được thực hiện là một phương thức bản địa, các nội dung thanh ghi pc của nó
– là màu tối (không xác định).
2.1.1. Bộ nhớ Heap
Bất cứ một lớp hay một mảng nào được tạo ra trong quá trình chạy của máy ảo
Java, bộ nhớ để lưu trữ tất cả các đối tượng là Heap. Java có chương trình chạy riêng
biệt nên không thể có việc hai chương trình Java khác nhau lại sử dụng chung một dữ
liệu, nhưng hai chủ đề trong cùng một ứng dụng có thể xung đột dữ liệu đó là việc bạn
phải quan tâm đến việc đồng bộ hoá và tiếp cận đa luồng cho các đối tượng sử dụng
trong chương trình Java của bạn.
Các máy ảo Java cấp phát heap cho các đối tượng mới nhưng không có hỗ trợ
phương thức cho người sử dụng giải phóng nó. Thông thường các máy ảo Java sẽ sử
dụng bộ thu dọn rác để quản lý Heap
a) Bộ thu dọn rác
Chức năng chính của chương trình là lấy lại các vùng nhớ đang được sử dụng bời
các đối tượng mà không còn được tham chiếu đến nữa khi JVM đang hoạt động. Nó
cũng có thể di chuyển các ứng dụng hay các đối tượng chạy để giảm phân mảnh.
Có thể bộ thu dọn rác hoạt động không đúng đặc điểm kỹ thuật của máy ảo Java.
Các đặc điểm kỹ thuật chỉ yêu cầu thực hiện quản lý Heap riêng của mình theo một số
phương pháp. Các đặc điểm kỹ thuật của máy ảo Java không nói có bao nhiêu bộ nhớ
phải được sử dụng để có sẵn đối tượng cho chương trình đang chạy. Nó không nói làm
thế nào để quản lí thực thi bộ nhớ Heap của nó, nó nói với các nhà thực hiện là chương
trình sẽ được cấp phát bộ nhớ nhưng không giải phóng nó.
Không có kỹ thuật thu dọn nào được quyết định bởi các đặc điểm kỹ thuật của máy
ảo Java. Nhà thiết kế sử dụng bất cứ kỹ thuật nào mà họ cho là phù hợp với mục tiêu
và với những hạn chế về trình độ của họ.
Khi một máy ảo cần một bộ nhớ cho một đối tượng mới, nó có thể làm mất dữ liệu
từ Heap mà đối tượng đang cư trú. Việc thu dọn rác và giải phóng bộ nhớ bị quản lí
bởi các đối tượng unreferenced có thể chăm sóc việc tìm kiếm và giải phóng. Triển Page | 12
Tìm hiểu JVM
khai cho phép người sử dụng hay lập trình có thể dễ dàng xác định 1 kích thước ban
đầu cho Heap.
b) Các đối tượng (Object)
Các đặc điểm kỹ thuật của máy ảo java là im các đối tượng trên Heap. Đối tượng
đại diện - một khía cạnh của thiết kế tổng thể - là quyết định của các nhà thiết kế thực
hiện.
Các dữ liệu chính phải bằng cách nào đó được đại diện cho mỗi đối tượng là các
biến được khai báo trong các class của đối tượng và tất cả các superclasses của nó.
Cho một tham chiếu đối tượng, các máy ảo phải nhanh chóng xác định vị trí các dữ
liệu mẫu cho đối tượng. Ngoài ra phải có một số cách để truy cập dữ liệu lớp của đối
tượng(được lưu trữ trong khu vực phương thức) cho tham chiếu đến đối tượng. Vì lý
do này cấp phát bộ nhớ cho một đối tượng thường bao gồm một số loại con trỏ vào
khu vực phương thức.
Một thiết kế Heap có thể chia heap ra làm hai thành phần : Một khu xử lý và một
khu đối tượng. Một tham chiếu đối tượng là một con trỏ có nguồn gốc từ một khu xử
lý. Một khu xử lý có hai thành phần: một con trỏ đến đối tượng mẫu trong khu đối
tượng và một con trỏ đế dữ liệu mẫu ở khu phương thức. Ưu điểm của hệ thống này là
làm cho nó dễ dàng chống lại việc phân mảnh Heap. Khi các máy ảo di chuyển đối
tượng ra một vị trí mới trong khu đối tượng nó chỉ việc trỏ đến nơi mới của đối tượng.
Nhưng bất lợi của phương pháp này là mỗi một lần truy cập vào dữ liệu lại yêu cầu sử
dụng 2 con trỏ. Hình ảnh sau đây thể hiện rõ ràng cách hoạt động của phương pháp
này.
Page | 13
Tìm hiểu JVM
Hình 2.3 Mô tả cách tiếp cận đối tượng của Heap
Một thiết kế khác là làm cho đối tượng tham chiếu một con trỏ có nguồn gốc từ
một gói dữ liệu đã có dữ liệu sẵn. Cách tiếp cận này đòi hỏi dereferencing chỉ có một
con trỏ để truy cập dữ liệu. Khi các máy ảo di chuyển đối tượng dẫn đến việc bị phân
mảnh Heap, nó phải cập nhập mỗi tham chiếu đến đối tượng mà bất cứ nơi nào trong
khu vực dữ liệu thời gian chạy. Cách tiếp cận này được thể hiện như sau.
Page | 14
Tìm hiểu JVM
Hình 2.4 Một cách tiếp cận Object khác của Heap
Các máy ảo cần nhận được đối tượng tham chiếu đến lớp kiểu dữ liệu của đối
tượng đó vì nhiều lý do. Khi một chương trình cố gắng để một đối tượng tham chiếu
đến một kiểu khác, các máy ảo phải kiểm tra xem các loại đang được tham chiếu đến
là class của đổi tượng tham chiếu hoặc là một trong các siêu kiểu của nó. Nó phải được
kiểm tra khi một chương trình được chạy hay một instanceof hoạt động . Trong cả hai
trường hợp, các máy ảo phải chọn lựa phương pháp nào để gọi không dựa trên tham số
nhưng trên lớp của đối tượng. Để làm điều này nó phải có quyền một lần nữa truy cập
vào các class dữ liệu nhất định chỉ có 1 tham chiếu đến đối tượng.
Một cách một thực hiện có thể kết nối với phương thức để đối tượng được tham
chiếu được hiển thị trong Hình 2.5 con số này cho thấy rằng con trỏ giữ cùng với các
dữ liệu ví dụ cho từng đối tượng điểm đến một cấu trúc đặc biệt. Cấu trúc đặc biệt này
có 2 phần
Một con trỏ có đầy đủ dữ liệu cho lớp đối tượng
Page | 15
Tìm hiểu JVM
Bảng các phương thức cho các đối tượng bảng phương thức là một mảng
của con trỏ đến dữ liệu cho mỗi phương thức. Các dữ liệu được trỏ đến bởi
bảng phương thức bao gồm:
o Các kích thước của toán hạng Stack và biến địa phương của phương
thức ngăn xếp
o ByteCode của phương thức
o Bảng các giá trị ngoại lệ
Điều này tạo đầy đủ thông tin để máy ảo gọi phương thức.
Hình 2.5 Một cách tham chiếu đến đối tượng
c) Đại diện mảng
Trong Java mảng là đối tượng đầy đủ. Giống như đối tượng mảng cũng được
lưu trữ
trên Heap. Cũng như đối tượng các nhà thiết kế thực hiện có thể quyết định cách mà
họ đại diện cho mảng trên heap.
Page | 16
Tìm hiểu JVM
Mảng có class liên quan đến class của chúng giống như bất kỳ đối tượng khác.
Tất cả các mảng có cùng kích thước và cùng loại sẽ cùng một lớp. Chiều dài của một
mảng (hay là độ dài kích thước của mảng đa chiều) không đóng vai trò nào trong việc
thành lập nên lớp mảng. Chiều dài của một mảng coi như là một phần dữ liệu của
mảng
Hình 2.6 Đại diện của mảng trên Heap
Các dữ liệu lưu trữ trên heap cho mỗi mảng là chiều dài của mảng, mảng dữ
liệu, và một số loại tham số lớp của mảng. Cho một tham chiếu đến mảng, các máy ảo
phải có khả năng xác định độ dài của mảng, để có được và cài đặt các phần tử của nó
bằng chỉ số(kiểm tra để chắc chắn không vượt quá giới hạn) và để gọi bất kỳ một
phương thức kê khai nào của đối tượng các lớp cha trực tiếp của tất cả các mảng.
2.2.2 Stack trong Java
Khi một thread được đưa ra, các máy ảo Java tạo ra một Java Stack mới cho
thread. Như đã đề cập trước đó một Stack lưu trữ trạng thái của một thread trong
khung rời rạc. Các máy ảo Java chỉ thực hiện hai hoạt động trực tiếp trên Stack: Đẩy
và Bật khung
Các phương thức đang được thực hiện bởi một thread là phương thức hiện
hành. Các khung stack cho các phương thức tại thời điểm là phương thức hiện tại. Các Page | 17
Tìm hiểu JVM
lớp chứa các phương thức hiện tại là lớp hiện tại và khu chứa các lớp hiện tại gọi là
khu hiện tại. Khi thực hiện một phương thức các máy ảo theo dõi các lớp hiện tại và
các khu hiện tại. Khi máy ảo gặp hướng dẫn hoạt động trên các dữ liệu được lưu trữ
trên các khung stack, nó thực hiện hoạt động trên khung hiện tại.
Khi một thread gọi một phương thức Java, máy ảo tạo ra và đẩy một khung mới
vào thread của Java Stack. Khung mới này sau đó trở thành nhưng khung hiện tại.
Phương thức thực hiện, nó sử dụng khung hình để lưu trữ các thông số, các biến địa
phương, tính toán trung gian và các dữ liệu khác.
Một phương thức có thể hoàn thành bằng một trong hai cách. Nếu một phương
thức hoàn thành bằng cách quay lại nó được cho là hoàn thành bình thường. Nhưng
nếu kết thúc bằng cách ném ra một ngoại lệ nó được cho là hoàn thành đột ngột. Khi
một phương thức hoàn thành cho dù là bình thường hay đột ngột các Java pops của
máy ảo sẽ loại bỏ khung của khương thức ra khỏi Java Stack. Các khung hình cho các
phương thức trước đó sẽ trở thành khung hiện tại.
Tất cả dữ liệu trên một thread của Java Stack là dựa trên một thread nào đó.
Không có cách nào để thread truy cập cũng như thay đổi Stack Java của một thread
khác. Bởi vì điều này bạn không bao giờ phải lo lắng đồng bộ hoá luồng truy cập đến
biến địa phương trong chương trình của bạn. Một khi thread gọi một phương thức biến
cục bộ của phương thức được lưu trữ trong một khung trên thread theo Java Stack. Chỉ
có phương thức của thread mới truy cập được vào biến địa phương trên thread đó.
Cũng giống như các phương thức và heap, Java stack và khung stack không
phải được tiếp giáp trong bộ nhớ. Khung có thể được phân bổ trên một Stack tiếp giáp
hoặc trên heap hoặc cả hai. Các cấu trúc dữ liệu thực tế được sử dụng đại diện cho
stack Java và khung Stack là quyết định của người thiết kế thực hiện. Triển khai có thể
cho phép người sử dụng hoặc lập trình xác định một kích thước ban đầu cho Stack
Java cũng như các giá trị kích thước max min.
a) The Stack Frame (Khung stack)
Các khung stack có 3 phần: các biến địa phương, toán hạng stack và khung dữ
liệu. Các kích thước của các biến địa phương và toán hạng stack phụ thuộc vào nhu
Page | 18
Tìm hiểu JVM
cầu của từng phương thức riêng lẻ. Các kích thước này được xác định tại thời điểm
biên dịch và đưa vào class dữ liệu cho từng phương thức. Kích thước của khung dữ
liệu là sự phụ thuộc
Khi máy ảo Java chạy sẽ là một phương thức Java, nó sẽ kiểm tra class dữ liệu để
xác định số lượng các từ theo yêu cầu của phương thức trong các biến địa phương và
toán hạng stack. Nó tạo ra một khung stack có kích thước thích hợp và đẩy vào Stack
Java.
b) Các biến địa phương
Phần biến địa phương của stack frame Java được tổ chức như một mảng số không
dựa trên các từ. Sử dụng một giá trị từ biến địa phương cung cấp một chỉ số vào mảng
zero-based. Giá trị kiểu int, float, reference và returnAddress chiếm một ô trong mảng
các biến địa phương. Giá trị kiểu byte, short và char được chuyển sang int trước khi
lưu vào biến địa phương. Giá trị của kiểu long và double chiếm 2 mục liên tiếp trong
mảng.
Phần biến cục bộ chứa các thông số của một số phương thức và các biến địa
phương. Trình biên dịch đặt các tham số vào mảng biến địa phương đầu tiên theo thứ
tự chúng được khai báo.Hình 2.7 cho thấy phần biến địa phương trong 2 phương pháp.
Page | 19
Tìm hiểu JVM
Hình 2.7 Tham số phương thức trên phần biến địa phương của Java Stack
Lưu ý rằng hình 2.7 cho thấy các tham số đầu tiên trong các biến địa phương cho
runInstanceMethod() là kiểu reference mặc dù không có các tham số như vậy xuất hiện
trong mã nguồn. Phương thức sử dụng thông tin này để truy cập các dữ liệu thể hiện
các đối tượng trên đó. Như bạn có thể thấy bằng cách nhìn vào các biến cục bộ của
runClassMethod() trong trong hình 2.7, các phương thức lớp không nhận được ẩn này.
Phương thức Class không được gọi trên đối tượng. Bạn có thể không trực tiếp truy cập
vào các biến cá thể của một class từ một phương thức vì không có sự kết hợp với các
phương thức gọi.
Cũng lưu ý rằng các loại byte, short, char và boolean trong mã nguồn mở trở thành
int s trong biến địa phương. Điều này cũng đúng với các toán hạng stack. Như đã đề
cập trước đó, boolean là loại không được hỗ trợ trong máy ảo Java. Trình biên dịch
luôn luôn sử dụng int s để đại diện cho giá trị boolean trong các biến địa phương hay
các toán hạng stack. Các kiểu byte, short và char tuy được hỗ trợ trực tiếp bởi máy ảo
Java. Chúng có thể được lưu trữ trên heap như các biến cá thể hoặc các phần tử mảng
hoặc trong khu vực như phương thức lớp. Khi được đặt vào biến địa phương hoặc các
toán hạng stack thì các biến kiểu byte short hay char cũng được chuyển thành int s.
Page | 20
Tìm hiểu JVM
Chúng lưu int trên các khung stack sau đó mới chuyển đổi thành byte, short hay char
khi lưu trữ trên heap hoặc phương thức địa phương.
Cũng lưu ý rằng đối tượng được thông qua như là một tham chiếu đến
runClassMethod() trong Java tất cả đối tượng được thông qua như là tham chiếu. Như
tất cả các đối tượng được lưu trữ trên heap, bạn sẽ không bao giờ tìm thấy hình ảnh
của một đối tượng trong các biến địa phương hoặc toán hạng stack, chỉ phản đối tham
chiếu.
Bên cạnh các thông số của một phương thức, trong đó trình biên dịch phải đặt vào
mảng các biến địa phương đầu tiên và để khai báo, trình biên dịch Java có thể sắp xếp
các mảng các biến đia phương như họ muốn. Trình biên dịch có thể đặt các biến cục
bộ của phương thức vào mảng theo thứ tự nào và họ có thể sử dụng các mục cùng một
mảng cho nhiều hơn một biến địa phương.
Như tất cả các khu vực nhớ thời gian chạy khác, các nhà thiết kế có thể sử dụng bất
cứ cấu trúc dữ liệu mà họ cho là thích hợp nhất để đại diện cho biến địa phương. Các
đặc điểm kỹ thuật của máy ảo Java không sử dụng cho kiểu long và kiểu double nên
được chia trên hai mục mà mảng chiếm đóng. Sử dụng 64bit để lưu trữ 2 kiểu này.
c) Toán hạng stack
Giống như các biến địa phương các toán hạng stack được tổ chức giống như một
mảng các từ. Nhưng không giống các biến địa phương được truy cập thông qua chỉ số
của mảng, các toán hạng stack được truy cập bằng cách push và pop các giá trị. Nếu
một lệnh là push một giá trị vào các toán hạng stack một thời gian sau có thể lấy và sử
dụng giá trị đó.
Các máy ảo lưu trữ cùng loại dữ liệu trong ngăn xếp mà nó lưu trữ các biến địa
phương: int, long, float, double, reference và kiểu biến trả về (return type). Nó chuyển
các giá trị kiểu byte, short, char thành kiểu int trước khi đưa vào toán hạng stack.
Khác với các chương trình truy cập, không được truy cập trực tiếp bởi các chỉ thị
các máy ảo Java không có thanh ghi. Các máy ảo là một stack hơn là một thanh ghi bởi
vì nó lấy các toán hạng từ stack chứ không phải từ thanh ghi. Hàm cũng có thể lấy toán
Page | 21
Tìm hiểu JVM
hạng từ những nơi khác, chẳng hạn như ngay lập tức sau opcode trong dòng bytecode.
Các máy ảo Java tập trung vào các tập lệnh nhưng là các toán tử stack.
Các máy ảo Java sử dụng ngăn xếp toán hạng như một không gian làm việc. Các
hàm pop các giá trị từ toán hạng stack hoạt động trên chúng và push các kết quả.
Trong hình 2.8 bạn có thể thấy được sự miêu tả hoạt động các biến địa phương và
stack khi thực hiện hàm.
Hình 2.8 Mô phỏng việc thêm 2 biến cục bộ
d) Các triển khai có thể có của Stack
Nhà thiết kế có thể thiết kế Stack Java bất cứ như thế nào mà họ muốn. Như đã nói
từ trước, một cách thực hiện các stack là bằng cách phân bổ mỗi khung hình riêng biệt
từ heap.
Ví Dụ:
Page | 22
Tìm hiểu JVM
class Example3c {
public static void addAndPrint() { double result = addTwoTypes(1, 88.88); System.out.println(result); }
public static double addTwoTypes(int i, double d) { return i + d; }}
Hình sau đây cho thấy ba bức ảnh chụp của stack Java cho một thread để gọi
phương thức addAndPrint (). Trong việc thực hiện của các máy ảo Java bên trong
hình, mỗi khung hình được phân bổ riêng biệt từ heap. Để gọi các phương thức
addTwoTypes() và addAndPrint(). Phương thức đầu tiên push một int 1 và double
88,88 vào toán hạng của stack. Sau đó nó gọi phương thức addTwoTypes()
Hình 2.9 Mô phỏng hoạt động stack qua Ví Dụ
Các hàm để gọi addTwoTypes() dùng để chỉ 1 khung liên tuc. Các máy ảo Java
trong lên các mục và giải quyết nó nếu cần thiết
Hình 2.10 cho thấy stack Java của các máy ảo khác nhau thực hiện các phương
thức tương tự. Thay vì phân bổ mỗi khung hình riêng biệt từ heap, điều này thực hiện Page | 23
Tìm hiểu JVM
phân bổ các khung hình từ một ngăn xếp liền kề nhau. Cách tiếp cận này cho phép
thực hiện ghi đè lên khung của phương thức lân cận. Các phần của phương thức gọi
toán hạng stack chứa các tham số cho các phương thức trở thành cơ sở của các biến địa
phương. Trong ví dụ này addAndPrint() ghi đè addTwoTypes toàn bộ biến địa
phương.
Hình 2.10 Phân bố các khung hình từ ngăn xếp liền kề nhau
Cách tiếp cận này giúp tiết kiệm không gian bộ nhớ vì bộ nhớ tương tự được sử
dụng bởi các phương thức gọi để lưu trữ các thông số như được sử dụng theo phương
pháp viện dẫn để truy cập vào các thông số. Nó giúp tiết kiệm thời gian vì các máy ảo
Java không phải mất thời gian sao chép các giá trị tham số từ một khung hình khác.
Lưu ý rằng ngăn xếp toán hạng của khung hiện tại luôn luôn ở “ top” Stack Java.
Mặc dù điều này có thể được dễ dàng hơn để hình dung trong việc thực hiện bộ nhớ kề
Page | 24
Tìm hiểu JVM
nhau của hình 2.10, đó là sự thật không có vấn đề làm thế nào stack Java được thực
hiện.
e) Native Method Stack()
Ngoài tất cả các lĩnh vực dữ liệu thời gian chạy được xác định bởi các đặc điểm kỹ
thuật máy ảo Java và mô tả trước đây, một ứng dụng Java chạy có thể sử dụng các
vùng dữ liệu khác được tạo ra bởi các phương thức địa phương. Khi một thread gọi
một phương thức địa phương, nó bước vào một thế giới mới, trong đó các cấu trúc và
các hạn chế bảo mật của máy ảo Java không còn cản trở sự tự do của mình. Một
phương thức địa phương có khả năng có thể truy cập vào các lĩnh vực dữ liệu thời
gian chạy của máy ảo (nó phụ thuộc vào giao diện phương thức điak phương), nhưng
cũng có thể làm bất cứ điều gì khác mà nó muốn. Nó có thể sử dụng thanh ghi bên
trong bộ vi xử lý địa phương, cấp phát bộ nhớ trên bất kỳ số lượng của heap địa
phương, hoặc sử dụng bất kỳ loại stack.
Nhà thiết kế thực hiện được tự do quyết định những cơ chế mà họ sẽ sử dụng để
cho phép một ứng dụng Java chạy trên việc thực hiện chúng để gọi các phương thức
địa phương.
Bất kỳ giao diện phương thức địa phương sẽ sử dụng một số loại stack. Khi một
thread gọi một phương thức Java, máy ảo tạo ra một khung mới và đẩy nó vào stack
Java. Khi một thread gọi một phương thức địa phương, tuy nhiên, thread ra sau Stack
Java. Thay vì đẩy một frame mới vào thread của Java stack, các máy ảo Java sẽ chỉ
đơn giản là tự động liên kết đến và trực tiếp gọi phương thức địa phương. Một cách để
nghĩ về nó là máy ảo Java là tự động mở rộng chính nó với mã nguồn gốc. Đó là, nếu
như thực hiện máy ảo Java là chỉ cần gọi đến một phương thức khác trong chính nó
khi chương trình Java chạy.
Nếu nguồn gốc giao diện phương thức của một hành động sử dụng một mô hình C-
liên kết, sau đó ngăn xếp phương thức địa phương là C ngăn xếp. Khi một chương
trình C gọi một hàm C, stack hoạt động theo một cách nhất định. Các tham số cho hàm
được đẩy vào ngăn xếp theo một thứ tự nhất định. Giá trị trả lại được chuyển lại cho
Page | 25
Tìm hiểu JVM
các chức năng cách gọi theo một cách nhất định. Đây có thể là hành vi của các stack
phương thức địa phương trong việc thực hiện điều đó.
Một giao diện thức địa phương sẽ có khả năng (một lần nữa, đó là các nhà thiết kế
để quyết định) có thể gọi lại vào máy ảo Java và gọi một phương thức Java. Trong
trường hợp này, các thread trở thành Native Method Stack và đi vào stack Java.
Hình 2.11 Stack cho một thread khi Java gọi phương thức địa phương
Như mô tả trong hình 2.11, một thread đầu tiên gọi hai phương thức Java, thread
thứ hai vừa gọi một phương thức địa phương. Hành động này gây ra các máy ảo để sử
dụng một Native Method Stack. Trong hình này, các phương thức stack địa phương
được hiển thị như là một số lượng hữu hạn của không gian bộ nhớ kề nhau. Giả sử nó
là một C stack. Các vùng stack sử dụng bởi mỗi chức năng C-liên kết được thể hiện
trong màu xám và bao quanh bởi một đường đứt. Các chức năng C-liên kết đầu tiên,
được gọi như là một phương thức địa phương, gọi một chức năng C-liên kết. Các chức
năng C-liên kết thứ hai gọi một phương thức Java thông qua giao diện phương thức địa
Page | 26
Tìm hiểu JVM
phương. Phương pháp Java này gọi một phương pháp Java, đó là phương thức hiện tại
được hiển thị trong hình vẽ.
Như với các khu vực bộ nhớ thời gian chạy khác, bộ nhớ của họ bị chiếm đóng bởi
heap phương thức địa phương không cần phải có kích thước cố định. Nó có thể mở
rộng và hợp nhất khi cần thiết bởi các ứng dụng đang chạy. Triển khai có thể cho phép
người sử dụng hoặc lập trình để xác định một kích thước ban đầu cho các khu vực
phương pháp, cũng như một kích thước tối đa hoặc tối thiểu.
2.2. The Class Loader
Một máy ảo Java có 2 bộ Class Loader: - Class Loader khởi động và Class Loader
được người dùng định nghĩa. Các Bootstrap Loader Class là một phần của việc thực
hiện máy ảo, và Class Loader người dùng định nghĩa là một phần của ứng dụng Java
chạy. Lớp được nạp bằng Class Loader khác nhau được đặt vào không gian riêng bên
trong máy ảo Java.
Các hệ thống phụ class loader liên quan đến nhiều bộ phận khác của máy ảo Java
và một số các Class từ thư viện java.lang. Ví dụ, Class Loader người dùng xác định
được các đối tượng Java thông thường, mà Class từ java.lang.ClassLoader Các
phương thức của lớp ClassLoader cho phép các ứng dụng Java để truy cập vào class
máy nạp của máy ảo. Ngoài ra, đối với mỗi loại nạp máy ảo, nó tạo ra một thể hiện của
lớp java.lang.Class để đại diện cho loại đó. Giống như tất cả các đối tượng, Class
Loader người dùng xác định và thể hiện của lớp cư trú trên heap. Dữ liệu với nhiều
loại tải nằm trong khu vực phương thức.
2.2.1. Loading, Linking and Initialization
Các hệ thống phụ class loader chịu trách nhiệm nhiều hơn chỉ là định vị và nhập dữ
liệu nhị phân cho các Class. Nó cũng phải xác minh tính đúng đắn của các lớp nhập
khẩu, cấp phát và khởi bộ nhớ cho các biến class, và hỗ trợ trong việc giải quyết các
vấn đề liên quan. Những hoạt động này được thực hiện theo một thứ tự nghiêm ngặt:
Loading: Tìm kiếm và nhập dữ liệu cho kiểu
Linking: Thực hiện xác minh, chuẩn bị và giải quyết
o Mã xác nhận: Bảo đảm tính chính xác của các kiểu biến
Page | 27
Tìm hiểu JVM
o Chuẩn bị: Phân bổ bộ nhớ cho các biến class và khởi tạo các bộ nhớ
có giá trị mặc định.
o Giải quyết: Chuyển các kiểu tham số tới và giải quyết trực tiếp.
Initialization: Mã Java khởi tạo các biến các và cho nó giá trị thích hợp
2.2.2. Bootstrap Loader Class
Java triển khai máy ảo phải có khả năng nhận biết và tải các lớp và giao diện
được lưu trữ trong các tập tin nhị phân mà phù hợp với các định dạng tập tin Class
Java. Một lần thực hiện là nhìn nhận các nhị phân khác bên cạnh các file class, nhưng
nó phải nhận ra các file class.
Mỗi Java thực hiện máy ảo có một bộ bootstrap class loader, mà biết làm thế
nào để tải các lớp tin cậy, bao gồm các Class của các API Java. Các đặc điểm kỹ thuật
máy ảo Java không định nghĩa thế nào là bootstrap loader cần xác định vị trí các class.
Đó là một quyết định đặc tả để các nhà thiết kế thực hiện.
Cho một tên biến đầy đủ, Bootstrap Class Loader phải trong một số cách nỗ lực
để xuất các dữ liệu định nghĩa các loại. Một phương pháp phổ biến được thể hiện
bằng Java thực hiện trong máy ảo 1.1 JDK của Sun trên Windows98. Điều này thực
hiện tìm kiếm một đường dẫn thư mục người dùng định nghĩa được lưu trữ trong một
biến môi trường có tên CLASSPATH. Bootstrap Loader tìm kiếm và xuất theo thứ tự
các thư mục xuất hiện trong CLASSPATH, cho đến khi nó tìm thấy một tập tin với tên
thích hợp: tên đầy đủ của biến đang tìm cộng với ".class". Trừ các kiểu là một phần
của gói tin giấu tên, Bootstrap Loader dự kiến các tập tin được trong một thư mục con
của một thư mục trong classpath. Các tên đường dẫn của thư mục con được xây dựng
từ các tên gói của kiểu. Ví dụ, nếu bootstrap class loader đang tìm cho class
java.lang.Object, nó sẽ tìm Object.class trong java \ lang thư mục con của mỗi
CLASSPATH thư mục.
Trong 1.2, Bootstrap Class Loader của của Sun Java 2 SDK chỉ về trong thư
mục mà trong đó các lớp hệ thống (các file class của Java API) đã được cài đặt. Các
Bootstrap Class Loader của các máy ảo Java từ Sun Java 2 SDK không nhìn vào
CLASSPATH. Trong Java 2 SDK máy ảo của Sun, tìm kiếm đường dẫn class là công Page | 28
Tìm hiểu JVM
việc của các system class loader, một class loader người dùng định nghĩa được tạo ra
tự động khi máy ảo khởi động.
2.2.3. Loader Class người dùng định nghĩa (user-defined class loader)
Mặc dù user-defined class loader được tự bản thân định nghĩa , 4 phương thức
của class ClassLoader như là 4 cổng vào của máy ảo
protected final Class defineClass(String name, byte data[], int offset, int length);protected final Class defineClass(String name, byte data[], int offset, int length, ProtectionDomain protectionDomain);protected final Class findSystemClass(String name);protected final void resolveClass(Class c);
Bất kì máy ảo Java nào phải quan tâm tới việc kết nối các phương thức của
ClassLoader hệ thống tới classloader nội bộ.
Hai phương thức defineClass () phương pháp chấp nhận một byte array, dữ liệu
[], như là đầu vào. Bắt đầu từ vị trí offset trong mảng và tiếp tục cho tới độ dài byte,
lớp ClassLoader hy vọng dữ liệu nhị phân phù hợp với các định dạng tập tin lớp Java -
dữ liệu nhị phân đại diện cho một kiểu mới cho các ứng dụng đang chạy - với tên đầy
đủ quy định trong tên. Các kiểu được gán như là 2 giá trị mặc định, nếu các bản đầu
tiên của defineClass () được sử dụng, hoặc tham chiếu đối tượng bởi
protectionDomain (). Mỗi máy ảo Java phải đảm bảo các phương thức defineClass ()
của lớp ClassLoader có thể tạo ra một dạng mới có thể cho vào khu vực phương thức
Các phương thức findSystemClass () chấp nhận một chuỗi là tên của một kiểu.
Khi Class Loader người dùng định nghĩa gọi phương thức này trong phiên bản 1.0 và
1.1, nó được yêu cầu rằng các máy ảo thử tất cả các loại trên qua bootstrap class. Nếu
Bootstrap Class Loader đã được nạp thành công hay tìm được kiểu, nó sẽ trả về một
tham chiếu đến các lớp đối tượng đại diện cho kiểu. Nếu nó không thể xác định vị trí
các dữ liệu nhị phân cho kiể, nó ném một ngoại lệ ClassNotFoundException. Trong
phiên bản 1.2, các phương thức findSystemClass () cố gắng tìm đúng yêu cầu từ hệ
thống. Mỗi máy ảo Java phải đảm bảo các phương thức findSystemClass ( ) có thể gọi
Page | 29
Tìm hiểu JVM
bootstrap (nếu phiên bản 1.0 hoặc 1.1) hoặc hệ thống (nếu phiên bản 1.2 hoặc mới
hơn) class loader theo cách này.
Các resolveClass () phương pháp chấp nhận một tham chiếu đến một biến thuộc
dạng class. Phương pháp này gây ra các đại diện lớp để được liên kết (nếu nó chưa
được liên kết). Các defineClass () phương pháp, mô tả trước đây, chỉ chăm lo công
việc tìm kiếm. Khi defineClass () trả về đối tượng lớp , các tập tin nhị phân cho các
kiểu đã chắc chắn được đặt và nhập khẩu vào khu vực phương thức, nhưng không nhất
thiết liên kết và khởi tạo. Máy ảo Java chắc chắn rằng triển khai các phương thức
resolveClass () của class ClassLoader có thể gây ra các hệ thống phụ class loader để
thực hiện kết nối.
2.2.4. Name Spaces
Mỗi Class Loader duy trì không gian cư trú riêng của các loại nó đã được nạp.
Bởi vì mỗi Class Loader có không gian riêng của mình, một ứng dụng Java đơn lẻ có
thể nạp nhiều loại với cùng tên. Tên đầy đủ của kiểu, do đó, không phải lúc nào cũng
đủ để nhận diện nó bên trong một máy ảo Java. Nếu nhiều loại cùng tên đã được nạp
vào không gian tên khác nhau, danh tính của bộ nạp lớp đó nạp các kiểu (danh tính của
không gian tên nó là bên trong) cũng sẽ là cần thiết để nhận diện kiểu đó.
Không gian tên phát sinh bên trong một máy ảo Java ví dụ như là kết quả của
quá trình phân giải. Là một phần của dữ liệu cho từng kiểu, các máy ảo Java theo dõi
các Class Loader khi nạp các kiểu. Khi máy ảo cần phải giải quyết một tham chiếu từ
một class khác, nó yêu cầu các class được tham chiếu từ các Class Loader tham chiếu
giống vậy.
2.3. Các Kiểu Dữ Liệu
Các máy ảo Java tính bằng cách thực hiện các hoạt động trên một số loại dữ
liệu. Cả hai loại dữ liệu và các hoạt động được quy định chặt chẽ bởi các đặc điểm kỹ
thuật máy ảo Java. Các kiểu dữ liệu có thể được chia thành một tập hợp của các loại
nguyên thủy và một kiểu tham chiếu. Biến của các loại nguyên thủy giữ giá trị nguyên
thủy, và các biến của các loại tham chiếu giữ giá trị tham chiếu. Giá trị tham chiếu là
tham chiếu đối tượng, nhưng không phải là đối tượng chính mình. Giá trị nguyên thủy,
ngược lại, không đề cập đến bất cứ điều gì. Chúng là những dữ liệu thực tếc của
Page | 30
Tìm hiểu JVM
chúng. Bạn có thể thấy một sự miêu tả đồ họa của gia đình các máy ảo Java của kiểu
dữ liệu trong hình 2.12.
Hình 2.12 Các kiểu dữ liệu trong Java
Tất cả các kiểu nguyên thủy của ngôn ngữ lập trình Java là các kiểu nguyên
thủy của các máy ảo Java. Mặc dù boolean đủ tiêu chuẩn là một kiểu nguyên thủy của
các máy ảo Java, hướng dẫn thiết lập đã hỗ trợ rất hạn chế cho nó. Khi một trình biên
dịch mã nguồn Java vào bytecode, nó sử dụng int s hoặc byte s để đại diện cho boolean
s. Trong các máy ảo Java, false được đại diện bởi số nguyên không và true đại diện
bởi bất kỳ số nguyên nào khác không. Hoạt động liên quan đến boolean giá trị sử dụng
int s. Mảng của boolean được truy cập như mảng của byte, mặc dù chúng có thể được
biểu diễn trên heap như mảng của byte hoặc như các bit.
Page | 31
Tìm hiểu JVM
Các loại nguyên thủy của ngôn ngữ lập trình Java khác hơn boolean dạng các
kiểu dữ liệu của các máy ảo Java. Các kiểu dữ liệu được chia sẻ giữa các loại tách rời:
byte, short, int, long, và char, và floating-point: float và double. Cũng như với các
ngôn ngữ lập trình Java, các loại nguyên thủy của các máy ảo Java có dải khắp mọi
nơi. Dạng long trong máy ảo Java luôn thực hiện như một số bù hai 64bit, độc lập với
các nền tảng máy chủ cơ bản.
Các máy ảo Java làm việc với một loại nguyên thủy khác là không có sẵn cho
các lập trình viên Java: các returnAddress loại. Loại nguyên thủy này được sử dụng để
thực hiện cuối cùng các điều khoản của chương trình Java.
Các kiểu tham số của máy ảo Java được đặt tên như tham biến. Giá trị của các
kiểu tham chiếu có 3 kiểu: dạng class, dạng interface, và dạng mảng array. Tất cả ba
loại đều có những giá trị mà là tham chiếu cho các đối tượng. Giá trị dạng class là
tham chiếu cho các class. Giá trị kiểu mảng của chiếu cho các mảng, mà là các đối
tượng chính thức trong các máy ảo Java. Giá trị dạng interface tham chiếu cho các lớp
mà thực hiện một giao diện. Một giá trị tham chiếu khác là không có giá trị, mà chỉ ra
các tham chiếu biến không đề cập đến bất kỳ đối tượng nào.
Các đặc điểm kỹ thuật máy ảo Java xác định phạm vi của các giá trị cho mỗi
loại dữ liệu, nhưng không xác định kích thước của chúng. Số lượng các bit được sử
dụng để lưu trữ giá trị của từng loại dữ liệu là một quyết định của các nhà thiết kế của
việc triển khai cá nhân. Các phạm vi của các kiểu dữ liệu của máy ảo Java được thể
hiện trong Bảng 2.1
Kiểu Phạm vi
Byte 8-bit đã ký hai của số nguyên bù (-2 7 để 2 7 - 1, bao gồm)
ngắn 16-bit đã ký hai của số nguyên bù (-2 15 để 2 15 - 1, bao gồm)
Int 32-bit đã ký hai của số nguyên bù (-2 31 để 2 31 - 1, bao gồm)
Dài 64-bit đã ký hai của số nguyên bù (-2 63 để 2 63 - 1, bao gồm)
char 16-bit ký tự Unicode không dấu (0-2 16 - 1, bao gồm)
phao 32-bit IEEE 754 chính xác đơn float
gấp đôi 64-bit IEEE 754 chính xác đôi nổiPage | 32
Tìm hiểu JVM
returnAddress địa chỉ của một opcode trong cùng một phương pháp
tham chiếu tham chiếu đến một đối tượng trên heap, hoặc rỗng
Bảng 2.1 Phạm vi các kiểu dữ liệu trong máy ảo Java
2.4. Word Size
Các đơn vị cơ bản của kích thước cho các giá trị dữ liệu trong máy ảo Java là từ
--a kích thước cố định được lựa chọn bởi các nhà thiết kế của mỗi Java thực hiện máy
ảo. Các kích thước Word phải đủ lớn để chứa một giá trị kiểu byte, ngắn, int, char,
float, returnAddress, hoặc tham chiếu. Hai từ phải đủ lớn để chứa một giá trị kiểu long
và double. Do đó một nhà thiết kế thực hiện phải chọn một cỡ chữ mà ít nhất là 32 bit,
nhưng nếu không có thể chọn bất cứ điều gì từ kích thước sẽ mang lại việc thực hiện
hiệu quả nhất. Các kích thước từ thường được chọn để được kích thước của một con
trỏ bản địa trên nền tảng máy chủ.
Các đặc điểm kỹ thuật của nhiều vùng dữ liệu thời gian chạy của máy ảo Java là
dựa trên các khái niệm trừu tượng này của một từ. Ví dụ, hai phần của một stack frame
Java - các biến địa phương và toán hạng stack-- được quy định trong các điều khoản
của các từ. Các khu vực này có thể chứa các giá trị của bất kỳ loại dữ liệu của máy ảo.
Khi được đặt vào các biến địa phương hoặc toán hạng stack, một giá trị chiếm một
hoặc hai từ.
Khi họ chạy, chương trình Java không thể xác định kích thước chữ của máy chủ
thực hiện máy ảo của họ. Các kích thước từ không ảnh hưởng đến hành vi của một
chương trình. Nó chỉ là một thuộc tính nội bộ của một máy ảo thực hiện.
2.5. Program Counter
Mỗi thread của một chương trình đang chạy có riêng register pc, hoặc truy cập
chương trình của nó, được tạo ra khi các thread được bắt đầu. Sổ register pc là một từ
trong kích thước, vì vậy nó có thể giữ cả một con trỏ bản địa và một returnAddress.
Như một thread thực thi một phương thức Java, register pc chứa địa chỉ của các hướng
dẫn hiện đang được thực thi bởi các sợi. Một "địa chỉ" có thể là một con trỏ bản địa
Page | 33
Tìm hiểu JVM
hoặc một offcode từ đầu bytecode của một phương thức Nếu một thread đang thực thi
một phương thức có nguồn gốc, giá trị của thanh ghi pc là không xác định.
2.6. Threads (luồng trong Java)
Các đặc điểm kỹ thuật máy ảo Java định nghĩa một mô hình luồng nhằm tạo
thuận lợi cho việc thực hiện trên một loạt các kiến trúc. Một mục tiêu của mô hình
luồng Java là cho phép các nhà thiết kế thực hiện, nếu có thể và thích hợp, sử dụng.
Ngoài ra, các nhà thiết kế có thể thực hiện một cơ chế luồng như là một phần của việc
thực hiện máy ảo của họ. Một ưu điểm của việc sử dụng luồng trên một máy chủ đa xử
lý là luồng khác nhau của một ứng dụng Java có thể chạy đồng thời trên bộ vi xử lý
khác nhau.
Một sự đánh đổi của mô hình luồng của Java là đặc điểm kỹ thuật của các ưu
tiên là mẫu số thấp nhất common-. Một luồng Java có thể chạy ở bất kỳ một trong
mười ưu tiên. Ưu tiên một là thấp nhất, và mười ưu tiên cao nhất. Nếu các nhà thiết kế
sử dụng luồng địa phương, họ có thể bản đồ mười ưu tiên Java vào các ưu tiên địa
phương có vẻ thích hợp nhất. Các đặc điểm kỹ thuật máy ảo Java định nghĩa hành vi
của luồng tại các ưu tiên khác nhau chỉ bằng cách nói rằng tất cả các luồng ở mức ưu
tiên cao nhất sẽ nhận được một số thời gian CPU. Luồng tại ưu tiên thấp hơn được
đảm bảo để có được thời gian CPU chỉ khi tất cả luồng đề ưu tiên cao hơn đều bị chặn.
Đề ưu tiên thấp hơn có thể nhận được một số thời gian CPU khi đề ưu tiên cao hơn
không bị chặn, nhưng không có bảo đảm.
Các đặc điểm kỹ thuật không thừa thời gian cắt giữa các luồng ưu tiên khác
nhau, bởi vì không phải tất cả các kiến trúc thời gian-slice. (Như được sử dụng ở đây,
thời gian cắt nghĩa rằng tất cả các luông ở tất cả các ưu tiên sẽ được đảm bảo một số
CPU thời gian, ngay cả khi không có luồng bị chặn.) Ngay cả trong số những kiến trúc
đó làm thời gian-slice, các thuật toán được sử dụng để phân bổ khe thời gian để luồng
tại ưu tiên khác nhau có thể khác nhau rất nhiều.
Trong các máy ảo Java Specification, hành vi của luông Java được định nghĩa
theo các biến, một bộ nhớ chính, và trạng thái làm việc cũ. Mỗi máy ảo Java có một bộ
nhớ chính, trong đó có chứa tất cả các biến của chương trình: các biến thể hiện của các
đối tượng, các thành phần của mảng, và các biến Class. Mỗi thread có một trạng thái
Page | 34
Tìm hiểu JVM
làm việc cũ. Biến và các tham số địa phương, bởi vì họ tin vào các luồng cá nhân,
logic có thể được xem như là một phần của một trong hai bộ nhớ làm việc hoặc bộ nhớ
chính.
Các đặc điểm kỹ thuật máy ảo Java định nghĩa nhiều quy tắc chi phối các mối
tương tác ở mức độ thấp của luồng với bộ nhớ chính. Ví dụ, một quy tắc rằng tất cả
các hoạt động trên các kiểu nguyên thủy, ngoại trừ trong một số trường hợp long s và
double s, là nguyên tử. Ví dụ, nếu hai luồng cạnh tranh để viết hai giá trị khác nhau về
một biến int , ngay cả trong trường hợp không đồng bộ, biến sẽ kết thúc với một giá trị
sai khác. Biến đó sẽ không có một giá trị bị lỗi. Nói cách khác, một trong những luồng
sẽ giành chiến thắng trong cạnh tranh và viết giá trị của nó để biến đầu tiên. Các luông
thua không cần phải phiền muộn, tuy nhiên, vì nó sẽ viết giá trị của nó vào biến thứ
hai, ghi đè lên biến của luồng “chiến thắng”.
Về cơ bản, các quy định về hành vi thread-mức thấp chỉ định khi một luồng có
thể và khi đó phải:
1. Sao chép các giá trị của các biến từ bộ nhớ chính để bộ nhớ làm việc của
mình.
2. Viết các giá trị từ bộ nhớ làm việc của mình trở lại vào bộ nhớ chính.
Đối với điều kiện nhất định, các quy tắc xác định một thứ tự chính xác và dự
đoán được bộ nhớ đọc và viết. Đối với các điều kiện khác, tuy nhiên, các quy tắc
không chỉ định bất kỳ trật tự. Các quy tắc được thiết kế để cho phép lập trình Java để
xây dựng các chương trình đa luồng mà biểu hiện hành vi dự đoán, trong khi cho các
nhà thiết kế thực hiện một cách linh hoạt. Sự linh hoạt này cho phép các nhà thiết kế
của Java triển khai máy ảo để tận dụng lợi thế của phần cứng và phần mềm tiêu chuẩn
kỹ thuật có thể cải thiện hiệu suất của các ứng dụng đa luồng.
Các cấp cao ý nghĩa cơ bản của tất cả các quy tắc ở mức độ thấp chi phối hành
vi của đề tài này là: Nếu truy cập vào các biến nhất định không đồng bộ, luồng được
phép cập nhật các biến trong bộ nhớ chính trong bất kỳ thứ tự. Nếu không đồng bộ,
các ứng dụng đa luồng của bạn có thể thể hiện hành vi đáng ngạc nhiên về một số việc
triển khai máy ảo Java. Với việc sử dụng hợp đồng bộ, tuy nhiên, bạn có thể tạo ra các
Page | 35
Tìm hiểu JVM
ứng dụng đa luồng Java mà cư xử một cách dự đoán về bất kỳ thực hiện của các máy
ảo Java.
2.7. Native Method Interface
Java triển khai máy ảo không cần thiết để hỗ trợ bất kỳ native method interface
đặc biệt. Triển khai một số có thể hỗ trợ không có native method interface ở tất cả.
Người khác có thể hỗ trợ một số, mỗi hướng với một mục đích khác nhau.
Java Native Interface của Sun, hoặc JNI, là hướng tới tính di động. JNI được
thiết kế để nó có thể được hỗ trợ bởi bất kỳ thực hiện của máy ảo Java, không có vấn
đề gì kỹ thuật thu gom rác thải hoặc đại diện đối tượng thực hiện sử dụng. Điều này sẽ
cho phép các nhà phát triển để liên kết cùng (JNI tương thích) binaries phương thức có
nguồn gốc từ bất kỳ JNI-hỗ trợ triển khai máy ảo trên một nền tảng máy chủ cụ thể.
Nhà thiết kế thực hiện có thể chọn để tạo ra các phương thức giao diện địa
phương độc quyền, thêm vào, hoặc thay thế, JNI. Để đạt được tính di động của nó, là
JNI sử dụng rất nhiều về con trỏ và hàm con trỏ đến các chức năng. Để có được hiệu
quả tuyệt đỉnh, các nhà thiết kế của một thực hiện có thể quyết định cung phương thức
giao diện địa phương ở mức độ thấp của riêng mình mà vẫn gắn chặt với các cơ cấu
thực hiện cụ thể của mình. Nhà thiết kế cũng có thể quyết định cung cấp một native
method interface cấp cao hơn JNI, chẳng hạn như một trong đó sẽ đem lại các đối
tượng Java thành một mô hình thành phần phần mềm.
Để làm việc hữu ích, một native method interface có khả năng tương tác ở mức
độ nào với các trạng thái nội bộ của máy ảo Java. Ví dụ, một native method interface
có thể cho phép các phương thức địa phương làm một số hoặc tất cả những điều sau
đây:
Duyệt và trả về dữ liệu (pass and return data)
Truy cập hoặc gọi các phương thức thu gom các đối tượng trên Heap
Biến class Access hoặc gọi phương thức class
Truy cập vào các mảng
Khoá một đối tượng trên Heap để sử dụng cho luồng hiện tại
Tạo một đối tượng mới trên Heap
Page | 36
Tìm hiểu JVM
Nạp các class mới
Ném ra ngoại lệ
Ném ngoại lệ được gọi bởi phương thức
Bắt ngoại lệ không đồng bộ và ném ra bởi máy ảo
Dọn dẹp các đối tượng mà không còn được sử dụng trên Heap
Thiết kế một Native Method Interface mà cung cấp các dịch vụ này có thể phức
tạp. Các thiết kế cần phải đảm bảo rằng các bộ thu rác không giải phóng bất kỳ đối
tượng đang được sử dụng bởi các phương thức địa phương. Nếu thu gom rác của một
thực hiện di chuyển các đối tượng để giữ cho heap phân mảnh ở mức tối thiểu, thiết kế
phương thức giao diện phải đảm bảo rằng một trong hai:
- Một đối tượng có thể được di chuyển sau khi tham chiếu của nó đã được
thông qua với một phương pháp bản địa.
- Bất kỳ đối tượng có tham chiếu đã được thông qua với một phương thức địa
phương được gắn cho đến khi trở về phương thức địa phương hoặc nếu
không biết nó được thực hiện với các đối tượng
.
Page | 37
Tìm hiểu JVM
TÀI LIỆU THAM KHẢO
[1] https://vncoding.wordpress.com/2016/03/30/cung-tim-hieu-ve-jvm/
[2] https://en.wikipedia.org/wiki/Java_virtual_machine
[3] http://daynhauhoc.com/t/java-virtual-machine-va-c-ch-ho-t-d-ng/15082
[4] INSIDE THE JAVA VIRTUAL MACHINE - Bill Venners.
Page | 38