Phát triển với Java thời gian thực, Phần 3: Viết, xác nhận hợp lệ và phân tích một ứng dụng Java thời gian thực

Khám phá các công cụ và các kỹ thuật để xác nhận hợp lệ và cải thiện chất lượng tất định của dịch vụ

Dựa trên hai bài viết trước trong loạt bài ba phần này và dựa trên loạt bài Java™ thời gian thực, bài viết này chỉ ra cách thiết kế, mã hóa, xác nhận hợp lệ, và phân tích một ứng dụng thời gian thực cơ bản. Trọng tâm là về các khía cạnh thực hành của việc xác nhận đạt được chất lượng tất định dịch vụ của một ứng dụng.

Alan Stevens, Kỹ sư phần mềm, IBM Hursley Lab

Alan Stevens gia nhập IBM vào năm 1988 và đã làm việc trên một loạt dự án hiệu năng phần mềm, bao gồm CICS và Java từ năm 1997. Anh hiện đang được giao dự án Thời gian thực của IBM.



Andrew Hall, Kỹ sư phần mềm, IBM

Andrew Hall gia nhập Trung tâm Công nghệ Java của IBM trong năm 2004, bắt đầu từ nhóm kiểm thử hệ thống, nơi anh đã làm việc trong hai năm. Anh đã trải qua 18 tháng trong nhóm dịch vụ Java, nơi anh gỡ lỗi hàng chục vấn đề về bộ nhớ riêng trên nhiều nền tảng. Anh hiện là một nhà phát triển trong nhóm đảm bảo tính tin cậy, sẵn sàng và khả năng dịch vụ của Java. Trong thời gian rảnh rỗi anh thích đọc sách, chụp ảnh và làm trò tung hứng



30 11 2011

Bài viết này, phần đăng thứ ba và cuối cùng trong loạt bài Phát triển với Java thời gian thực, chỉ dẫn cách thiết kế, viết, xác nhận hợp lệ, và phân tích một ứng dụng thời gian thực cơ bản. Chúng tôi sẽ minh họa:

  • Các yêu cầu thời gian và hiệu năng của ứng dụng.
  • Tại sao mã Java không-thời gian thực thông thường không thích hợp cho ứng dụng này.
  • Các kỹ thuật lập trình Java thời gian thực nào được chọn.
  • Những mối quan tâm để đạt được tính tất định.
  • Kiểm thử và xác nhận tính tất định của ứng dụng.
  • Các công cụ và các kỹ thuật để gỡ lỗi các vấn đề về tính tất định thời gian thực.
  • Các tùy chọn nâng cao khả năng dự báo.

Chúng tôi đã giữ cho thiết kế và mã ứng dụng này đơn giản để chúng ta có thể tập trung vào những mục tiêu trên. Mã nguồn đầy đủ có sẵn để tải về.

Ứng dụng trình diễn

Nhiệm vụ của chúng ta là tạo ra một ứng dụng Java, chạy trên một hệ điều hành Linux® cung cấp các số đo nhiệt độ với độ trễ thời gian thấp từ một thiết bị công nghiệp. Để tận dụng năng suất và tính khả chuyển của ngôn ngữ Java, mã nguồn được giữ càng giống với mã Java không-thời gian thực tiêu chuẩn càng tốt. Để duy trì hiệu quả của thiết bị, cần phải thực hiện đọc và phân phối kết quả cho các chương trình tiêu thụ, để đến lượt mình, các chương trình này kiểm soát tốc độ cấp nguồn và làm mát cho thiết bị. Yêu cầu là các số đọc nhiệt độ phải sẵn sàng trong vòng 5 mili giây. Ngay cả các độ trễ ngắn cũng có thể làm cho tốc độ cấp nguồn và làm mát trở nên kém tối ưu, làm giảm hiệu quả của thiết bị.

Mẫu hình phân phối dữ liệu cho các trình tiêu thụ ở những khoảng thời gian cách đều nhau với các độ trễ thời gian rất thấp là một hình mẫu chung trong các hệ thống thời gian thực; các dữ liệu có thể là giá cả của thị trường chứng khoán hoặc các tín hiệu radar. Phần 2 mô tả một số những lý do tại sao không thể viết một ứng dụng như vậy bằng mã Java truyền thống mà vẫn đáp ứng được các yêu cầu thời gian này. Bảng 1 tóm tắt những lý do này và liệt kê các giải pháp tương ứng có sẵn trong Java thời gian thực. (Không phải tất cả các triển khai thực hiện đều đưa ra cùng một nhóm giải pháp).

Bảng 1. Java thường so với Java thời gian thực
Vấn đề trong Java thường Giải pháp trong Java thời gian thực
Độ trễ do nạp lớpCó thể tránh được độ trễ bằng cách nạp trước các lớp cần thiết.
Độ trễ do biên dịch tức thời (JIT) Có thể tránh được độ trễ bằng cách biên dịch trước-thời gian (AOT) hoặc biên dịch tức thời không đồng bộ.
Hỗ trợ rất hạn chế cho việc ưu tiên luồng ở mức hệ điều hànhĐặc tả thời gian thực cho Java (RTSJ) bảo đảm có ít nhất 28 mức ưu tiên có sẵn.
Không hỗ trợ cho việc kiểm soát chính sách luồng không mặc địnhCác RTSJ cung cấp cho nhà lập trình tùy chọn này để chọn lựa — ví dụ, vào-trước-ra-trước (FIFO).
Độ trễ thu gom rác (GC) dài

RTSJ cung cấp các vùng bộ nhớ không heap (các vùng nhớ phạm vi (scoped memory) và vùng nhớ bất tử (immortal memory)) cho các ứng dụng NoHeapRealtimeThread (NHRT-Luồng thời gian thực không phải heap) độc lập với việc tạm dừng để thu gom rác (GC).

Bây giờ có sẵn các trình thu gom rác thời gian thực để làm giảm độ trễ GC cho các luồng ứng dụng.

Độ trễ do các luồng nhân (kernel) hệ điều hành và đảo ngược ưu tiên Các nhân thời gian thực ví dụ như các nhân được cung cấp trong các bản phân phối Linux thời gian thực đã được thiết kế để tránh các luồng nhân chạy lâu, để có đầy đủ quyền ưu tiên trước, và để tránh các đảo nghịch ưu tiên (được mô tả trong Phần 1).

Chọn mô hình lập trình Java thời gian thực nào?

Chính yêu cầu là mã của ứng dụng trình diễn (demo) nên giữ càng giống mã Java không-thời gian-thực tiêu chuẩn càng tốt có hệ quả trực tiếp đến sự lựa chọn của chúng ta về mô hình lập trình RTSJ: việc sử dụng các NHRT và các vùng bộ nhớ để tránh độ trễ GC không phải là một tùy chọn.

Nhờ sử dụng các RealtimeThread, chúng ta có quyền truy cập vào các phần mở rộng lập trình Java thời gian thực mà không có những phức tạp của lập trình NHRT. Việc sử dụng các NHRT buộc người lập trình phải kiểm soát bộ nhớ hơn là dựa vào việc thu gom rác tự động thông thường. Trong thực tế, phải sử dụng các vùng nhớ phạm vi (memory scopes) chứ không phải là vùng nhớ bất tử (immortal memory) với các NHRT, vì vùng nhớ bất tử là hữu hạn và cuối cùng sẽ cạn kiệt. Ngoài ra còn có những hạn chế về những lớp nào là "an toàn NHRT" — nghĩa là, không sử dụng hoặc tham chiếu các đối tượng trên heap chính, mà các NHRT không thể làm việc này.

Ngoài việc lập trình đơn giản hơn, chúng ta cũng có thể hy vọng:

  • Hiệu năng tốt hơn. Các đối tượng heap được tạo ra và tham chiếu nhanh hơn so với các đối tượng vùng nhớ bất tử hay vùng nhớ phạm vi do rào cản-bộ nhớ và các chi phí khác cần thiết cho các đối tượng không heap.
  • Việc gỡ lỗi dễ dàng hơn. Cần phải ngăn các NHRT không cho tham chiếu bộ nhớ heap, do đó cần gỡ lỗi các ngoại lệ truy cập bộ nhớ. Các lỗi thiếu bộ nhớ với các NHRT cũng có thể bị châm ngòi từ một trong các heap vùng nhớ bất tử hoặc vùng nhớ phạm vi.

Chọn JVM thời gian thực nào?

Hai gói IBM® JVM Java 6 có sẵn cho các chương trình thời gian thực trên Linux, có tên giống nhau nhưng lại có các khả năng khác nhau và mức thỏa hiệp hiệu năng/tính tất định khác nhau (xem Tài nguyên). Gói cần thiết để chạy các chương trình trình diễn trong bài viết này là IBM WebSphere® Real Time cho RT Linux. Nó cung cấp khả năng RTSJ, như là các RealtimeThread và lớp hẹn giờ-định kỳ mà chúng ta sẽ cần để triển khai thực hiện luồng đọc giá trị nhiệt độ của chúng ta, và nó cung cấp thời gian tạm dừng để thu gom rác (GC) ngắn nhất. Gói này chạy trên một nhân Linux thời gian thực và phần cứng đặc thù để cung cấp tính tất định cần thiết cho các ứng dụng thời gian thực cứng. (Gói phần mềm kia — IBM WebSphere Real Time cho Linux — hỗ trợ các ứng dụng thời gian thực mềm, cung cấp khả năng thông qua và khả năng mở rộng cao hơn. Nó không có các thư viện lập trình RTSJ. Nó có trình thu gom rác Metronome, với mục tiêu hạn chế các thời gian tạm dừng để thu gom rác trong khoảng 3 mili giây).


Thiết kế ứng dụng trình diễn

Nhược điểm chính của việc sử dụng các RealtimeThread thay cho các NHRT sẽ là những khoảng tạm dừng (ngắn) của trình thu gom rác thời gian thực mà các luồng của chúng ta sẽ phải gánh chịu. Với trình thu rác Metronome của WebSphere Real Time, chúng ta có thể dựa vào việc mã của ứng dụng này bị treo do tạm dừng để thu gom rác chỉ trong khoảng 500 micro giây. Và chúng ta biết rằng trong một chu kỳ thu gom rác ứng dụng của chúng ta sẽ được bảo đảm chạy được ít nhất 70% thời gian. Nói cách khác, trong một ô cửa sổ thời gian là 10 mili giây, chúng ta hy vọng có không quá sáu lượng tử thu gom rác khoảng chừng 500 micro giây mỗi lần, xen kẽ với thời gian của ứng dụng. Mô hình này được minh họa dưới đây, trong bài viết này.

Một yêu cầu đối với ứng dụng này là để cung cấp dữ liệu nhiệt độ ở các khoảng thời gian không quá 5 mili giây. Thiết kế này là để đọc nhiệt độ cứ mỗi 2 mili giây, đủ ngẫu nhiên để đáp ứng yêu cầu ngay cả khi các chu kỳ thu gom rác chạy.

Định nghĩa về chất lượng thời gian thực của dịch vụ

Khái niệm về chất lượng thời gian thực của dịch vụ làm nổi bật sự tương phản giữa tính tất định so với các yêu cầu hiệu năng thuần túy. Với một ứng dụng thời gian thực, chúng ta thường quan tâm đến việc bảo đảm tính chính xác và kịp thời của các hành vi hệ thống chứ không phải là tốc độ thông qua. Thật vậy, việc triển khai thực hiện Java thời gian thực sẽ chậm hơn việc triển khai thực hiện Java tiêu chuẩn có thể so sánh được, bởi vì các chi phí thêm cần thiết để hỗ trợ RTSJ (chẳng hạn như các rào cản bộ nhớ) và sự cần thiết phải vô hiệu hóa việc tối ưu hóa thời gian chạy mà tạo ra hành vi không tất định. Tùy thuộc vào việc liệu chúng ta đang xác định một hệ thống thời gian thực cứng hay một hệ thống thời gian thực mềm, yêu cầu này sẽ được diễn đạt khác nhau.

Trong một hệ thống thời gian thực cứng, hệ thống này phải tạo ra tất cả các dữ liệu nhiệt độ sẵn dùng cho các luồng tiêu thụ trong vòng 5 mili giây. Bất kỳ sự chậm trễ nào quá 5 mili giây sẽ tạo ra một lỗi hệ thống. Trong một hệ thống thời gian thực mềm, hệ thống này phải tạo ra hầu như tất cả các dữ liệu nhiệt độ sẵn dùng cho các luồng tiêu thụ trong vòng 5 mili giây. Với điều kiện rằng ít nhất 99,9% các số đọc được có sẵn trong vòng 5 mili giây, và 99,999% được phân phối trong vòng 200 mili giây, thì hệ thống này có thể chấp nhận được.

Xác nhận hợp lệ các hệ thống thời gian thực

Việc kiểm thử đơn giản chức năng hoặc hiệu năng của các ứng dụng thời gian thực vẫn chưa đủ để chứng tỏ rằng chúng đáp ứng được các yêu cầu của mình. Một hệ thống chạy chính xác trong một vài lần kiểm thử có thể làm suy giảm hay làm thay đổi kết quả đầu ra của nó theo thời gian. Sẽ không thể đưa ra vùng các giá trị có thể của thời gian thi hành hoặc lịch biểu định kỳ trừ phi lấy mẫu lớn hơn nhiều qua các lần chạy kéo dài.

Ví dụ, từ yêu cầu thời gian thực mềm nói trên, chúng ta biết rằng chúng ta phải kiểm thử ít nhất 100.000 lần trước khi chúng ta bắt đầu chứng tỏ rằng đã đáp ứng được mục tiêu 99,999% trong vòng 200 mili giây. Đối với yêu cầu thời gian thực cứng, các nhà phát triển hệ thống phải gánh vác trách nhiệm này cần tiến hành kiểm thử đủ để cho phép họ chứng nhận rằng hệ thống này sẽ đáp ứng được yêu cầu tạo ra tất cả các dữ liệu nhiệt độ sẵn sàng cho các luồng tiêu thụ trong vòng 5 mili giây. Điều này thường được thực hiện bằng cách kiểm tra lâu dài kết hợp với nghiên cứu thống kê của phân phối dữ liệu thu được khi thực hiện. Hiện có các công cụ để tạo các đường phân phối phù hợp nhất với các mẫu dữ liệu, và dữ liệu đã mô hình hóa có thể được sử dụng để dự báo số lượng và khả năng xảy ra các sự kiện ngoại cảnh cực đoan. Vì chúng ta thường không có thời gian để kiểm thử hệ thống đủ lâu như chúng được triển khai, áp dụng trường hợp ngẫu nhiên vượt ra ngoài ngoại cảnh xấu nhất quan sát được là một cách tiếp cận thực tế để định nghĩa một hệ thống được cấp giấy chứng nhận.


Triển khai thực hiện ứng dụng trình diễn

Để đơn giản, chúng ta sẽ chỉ xem xét mã được các luồng sử dụng để phân phối dữ liệu nhiệt độ. Không mô tả chi tiết các luồng tiêu thụ dữ liệu ở đây; có thể giả định là chúng chạy trong các tiến trình riêng biệt trên cùng một máy tính.

Đối với việc triển khai thực hiện lần đầu, chúng ta sẽ không cố gắng tối đa hóa tính tất định, ngoài việc sử dụng công cụ biên dịch WebSphere Real Time AOT là admincache. Ta nhấn mạnh vào các kỹ thuật và các công cụ để nhận biết các vùng không tất định còn lại và các cách giải quyết chúng.

Chúng ta sử dụng một tiến trình Reader (Trình đọc) để thăm bộ cảm biến nhiệt độ của thiết bị (ở đây được mô phỏng bằng một bộ tạo số ngẫu nhiên) cứ mỗi 2 mili giây. Lớp javax.realtime.PeriodicTimer là giải pháp của RTSJ để thực hiện đều đặn một hành động, và tiến trình Reader sử dụng PeriodicTimer để thăm bộ cảm biến nhiệt độ. Điều này được thực hiện như một BoundAsyncEventHandler để có độ trễ thấp nhất. Tiến trình Reader xây dựng một đoạn mã XML cho mỗi số đọc được và viết nó vào một ổ cắm mạng được kết nối với một tiến trình Writer (Trình viết). Nếu thời gian dành cho chu kỳ đọc các cảm biến và viết dữ liệu vượt quá 2 mili giây, thì có thông báo một lỗi.

Tiến trình Writer chạy trong một JVM riêng biệt trên cùng một máy tính như tiến trình Reader và nghe trên một ổ cắm mạng về các số đọc nhiệt độ. Tiến trình Writer sử dụng một luồng javax.realtime.RealtimeThread để nghe ổ cắm mạng nhằm lợi dụng mô hình lịch trình FIFO và kiểm soát mức ưu tiên với độ mịn cao. Nó giải nén đoạn mã XML, trích ra các số đọc nhiệt độ, và viết chúng vào một tệp nhật ký. Để làm cho dữ liệu sẵn sàng kịp thời cho trình tiêu thụ, tiến trình Writer cũng có một thời hạn chót là 3 mili giây để viết bất kỳ số đo nào ra đĩa. Nếu tổng thời gian kể từ lúc đọc được nhiệt độ cho đến lúc dữ liệu được viết ra vượt quá 5 mili giây, thì sẽ có thông báo một lỗi.

Hình 1 cho thấy dòng dữ liệu của ứng dụng:

Hình 1. Dòng dữ liệu
Dòng dữ liệu

Lưu ý rằng việc sử dụng XML cho một số lượng nhỏ dữ liệu như vậy trong một kịch bản nhạy cảm về thời gian không phải là cách thực hành thiết kế tốt. Ứng dụng trình diễn được xây dựng để đưa ra một ví dụ có ích cho việc khám phá hiệu năng của WebSphere Real Time. Không nên dùng nó như là một ví dụ tốt về một ứng dụng theo dõi nhiệt độ từ xa.

Chạy trình diễn

Nếu bạn có một môi trường WebSphere Real Time, bạn có thể tự chạy bản trình diễn của mình:

  1. Giải nén mã nguồn của bản trình diễn vào đâu đó trên máy WebSphere Real Time của bạn.
  2. Biên dịch bản trình diễn bằng lệnh:
    PATH to WRT/bin/javac -Xrealtime *.java
  3. Bắt đầu tiến trình Writer, nhập số cổng và tên tệp nhật ký cho nó. Ví dụ:
    sdk/jre/bin/java -Xrealtime Writer 8080 readings.txt
  4. Bắt đầu tiến trình Reader, nhập tên máy chủ và số cổng của tiến trình Writer. Ví dụ:
    PATH to WRT/jre/bin/java -Xrealtime Reader localhost 8080

Kết quả của bản trình diễn

Mã này ghi lại các độ trễ trong mỗi luồng Reader và Writer. Luồng Writer cũng ghi lại tổng thời gian truyền dữ liệu giữa việc đọc nhiệt độ và việc ghi vào đĩa. Để đáp ứng mục tiêu thời gian thực cứng của chúng ta, thời gian truyền dữ liệu này phải không (bao giờ) vượt quá 5 mili giây. Đối với thời gian thực mềm, 99,9%, trong vòng 5 mili giây đáp ứng phần đầu tiên của yêu cầu, và 99,999% trong vòng 200 mili giây đáp ứng phần thứ hai.

Trên hệ thống kiểm thử của IBM nơi ứng dụng này đã chạy, các đích thời gian thực mềm đã được đáp ứng, nhưng có các lỗi đối với đích thời gian thực cứng. Các phần tiếp theo thảo luận về một số công cụ và kỹ thuật có thể được sử dụng để điều tra kỹ các lỗi này.


Phân tích các chương trình Java thời gian thực

Các ràng buộc về thời gian thường dùng trong lập trình thời gian thực thường chặt chẽ hơn nhiều so với các ràng buộc về thời gian thường dùng để phân tích Java thông thường. Java thời gian thực thường làm việc với vùng nano giây-đến-mili giây, trái ngược với hàng chục mili giây và hơn nữa cho lập trình Java thông thường. Để hiểu các lỗi của ứng dụng trình diễn, chúng ta sẽ cần làm việc trong vùng micro giây và dưới mili giây.

Chúng ta đã nhận thấy rằng thất bại của bản trình diễn xảy ra phổ biến nhất trong phần ban đầu khi chạy thử. Đây là một phát hiện chung trong cả hệ thống thời gian thực lẫn trong hệ thống khác: chạy chậm nhất có xu hướng xảy ra ở một vài lần đầu tiên. Chúng ta đã đề cập đến một số các lý do cho việc này: việc nạp lớp (classloading) và việc biên dịch JIT.

Theo vết nạp lớp

Ở mức độ đơn giản nhất, chúng ta có thể chạy ứng dụng với tùy chọn dòng lệnh -verbose:class để xuất ra thông tin về tất cả các sự kiện nạp lớp (classloading). Tuy nhiên, để tương quan bất kỳ các sự kiện ngoại lệ nào với các hoạt động khác — ví dụ như nạp lớp — chúng ta cần các dấu thời gian (timestamp) chính xác về cả sự kiện ngoại cảnh lẫn nguyên nhân đáng ngờ. Với các mục đích chung, chúng ta có thể tự viết một công cụ bằng cách sử dụng sự kiện nạp lớp của giao diện JVMTI (Java Virtual Machine Tool Interface – Giao diện công cụ của máy ảo Java) (xem Tài nguyên), nhưng chúng ta vẫn sẽ cần cung cấp dụng cụ cho mã ứng dụng của chúng ta và cần tương quan các sự kiện được đánh dấu thời gian với nhau.

Theo vết việc biên dịch JIT

Các trình biên dịch JIT hiện đại cung cấp các lợi ích hiệu năng lớn. Về kỹ thuật chúng là phần mềm rất phức tạp mà hầu hết các nhà phát triển coi là các hộp đen. Chắc chắn là có ít sự tương tác rõ ràng thông qua các tùy chọn cấu hình, và ít khả năng nhìn thấy hoạt động JIT hơn so với trình thu gom rác chẳng hạn. Tuy nhiên, chúng ta có thể kích hoạt tính dài dòng của JIT bằng các tùy chọn dòng lệnh và các sự kiện JVMTI liên quan đến JIT sẽ có sẵn cho các công cụ để theo dõi việc tạo mã JIT.

Từ dòng lệnh, chúng ta có thể cho chạy JVM với cờ -Xjit:compiling để được thông báo về phương thức biên dịch (và biên dịch lại cho các mức tối ưu hóa cao hơn).

Cung cấp dụng cụ cho mã trình diễn

Giả sử rằng chúng ta đã kích hoạt cả hai cờ verbose:class-Xjit:compiling, nhưng chúng ta vẫn thấy các thất bại trong thời gian trình diễn xảy ra sau khi hoàn thành mọi việc nạp lớp và sau khi mã JIT sinh ra đã ổn định. Trong trường hợp này, chúng ta cần đi sâu hơn xem ứng dụng của chúng ta chính xác đang thực hiện những gì có liên quan đến hoạt động JVM khác.

Một cách tiếp cận là cung cấp dung cụ cho mã với các dấu thời gian để xác định xem độ trễ chủ yếu đang xảy ra ở đâu. Một lợi thế trên nền tảng Java thời gian thực là chúng ta có quyền truy cập tới các đồng hồ có hiệu năng cao, có độ chính xác cao. Chúng ta có thể thêm mã như sau vào các vùng nghi ngờ của chúng ta trong mã của tiến trình Reader:

AbsoluteTime startTime1 = clock.getTime();
xmlSnippet.append("<reading><sensor id=\"");
AbsoluteTime startTime2 = clock.getTime();
RelativeTime timeTaken = startTime2.subtract(startTime1);
System.err.println("Time taken: " + timeTaken);

Điều này có thể xác định các dòng mã chậm, nhưng đó là một quá trình vất vả và lặp lại, và dữ liệu được tạo ra không tương quan với bất kỳ các sự kiện nào khác. Nếu chúng ta dựa vào thứ tự của các sự kiện trên màn hình từ đầu ra này, ví dụ như, verbose:gc hoặc verbose:classloading, thì có thể có một số tiến bộ, nhưng có một giải pháp tốt hơn nhiều đó là: theo vết bằng Tuning Fork (N.D: Fork là lệnh tách đôi, sinh ra một tiến trình con mới).

Theo vết bằng Tuning Fork

Tuning Fork Visualization Platform - Nền tảng trực quan hóa Điều chỉnh âm thoa (xem Tài nguyên) ban đầu đã được xây dựng để giúp phát triển và gỡ lỗi cho trình thu gom rác Metronome. (Đây cũng là một trình cắm thêm Eclipse mở rộng và đã có những ứng dụng rộng rãi hơn, ví dụ, trong Bộ công cụ IBM để thu thập dữ liệu và phân tích trực quan cho các hệ thống đa lõi; xem Tài nguyên).

Những lợi thế của việc sử dụng Tuning Fork bao gồm:

  • Các phương tiện phân tích và hiển thị trực quan mạnh.
  • Có sẵn dữ liệu các hoạt động JVM: từ thu gom rác (GC), nạp lớp, biên dịch JIT, và các thành phần khác.
  • Kết hợp các điểm theo vết ứng dụng với dữ liệu theo vết JVM.

Những thay đổi mã cần thiết để bổ sung thêm các điểm theo vết ứng dụng Tuning Fork được đánh dấu trong mã nguồn được cung cấp kèm theo (các tệp Reader.java.instrumented và Writer.java.instrumented, xem Tải về).

Mã này phức tạp hơn một chút so với mã ví dụ trước về viết dữ liệu thời gian vào luồng đầu ra lỗi tiêu chuẩn, nhưng chúng tôi sẽ chỉ cho bạn thấy những lợi ích đáng kể về nỗ lực thêm này. Các điểm theo vết ứng dụng Tuning Fork có hai nhóm khác nhau: một nhóm ghi lại các sự kiện dấu thời gian đơn giản, còn nhóm kia ghi nhật ký dữ liệu thay mặt cho người lập trình. Cả hai sự kiện đều được ghi lại bằng cách sử dụng cùng các thành phần như các điểm theo vết JVM nội bộ cho hoạt động nạp lớp, JIT và GC. Quan trọng là điều này bảo đảm rằng cả hai, dữ liệu theo vết ứng dụng và dữ liệu theo vết JVM đều sử dụng cùng một máy đóng dấu thời gian — có nghĩa là chúng ta có thể so tương quan một cách an toàn tất cả các sự kiện với nhau bằng cách sử dụng các dấu thời gian để xem những gì đang chạy ở bất kỳ điểm nào. Những cố gắng xen kẽ dữ liệu theo vết từ các nguồn khác nhau thường đầy khó khăn và sai sót. Các sự kiện theo vết Tuning Fork duy nhất mà chúng ta cần ở đây là các sự kiện đánh dấu thời gian, mà chúng ta sẽ thêm vào tại điểm bắt đầu và kết thúc của các vùng mã ứng dụng ta quan tâm.

Mã bổ sung trong nguồn được đánh dấu bằng các ký tự phân cách, như trong ví dụ này:

/*---------------------TF INSTRUMENTATION START ------------------------- */
		writerTimer.start();
/* ---------------------TF INSTRUMENTATION END ------------------------- */

Mã nguồn được cung cấp dụng cụ tạo ra một tệp theo vết cho Reader JVM (Reader.trace) và một tệp cho Writer JVM (Writer.trace). Các tệp nhị phân này chứa các sự kiện bắt đầu và kết thúc việc xử lý tất cả các thông báo đọc nhiệt độ để phân tích sau này bằng trình hiển thị trực quan Tuning Fork.

Mã được thêm vào trong các phiên bản Tuning Fork có cung cấp dụng cụ là trong các lĩnh vực sau:

  • Các câu lệnh import các phương thức trong tệp tạo theo vết Tuning Fork, tuningForkTraceGeneration.jar
  • Mã khởi tạo cho một trình ghi nhật ký (logger) để viết và để tạo ra một bộ đếm giờ và feedlet (một nguồn cấp dữ liệu giữa bộ đếm giờ và trình ghi nhật ký).
  • Chạy khởi tạo thiết bị đo.
  • Một phương thức để buộc các feedlet vào luồng hiện tại.

Mã khác cần thiết duy nhất là thực hiện đếm giờ:

/*---------------------TF INSTRUMENTATION START ------------------------- */
                    writerTimer.start();
/* ---------------------TF INSTRUMENTATION END ------------------------- */

                    AbsoluteTime startTime = clock.getTime();

                    Code to be timed

                    RelativeTime timeTaken = stopTime.subtract(startTime);

/* ---------------------TF INSTRUMENTATION START ------------------------- */
                    writerTimer.stop();
/* ---------------------TF INSTRUMENTATION END ------------------------- */

Trong ví dụ của chúng ta, mã đếm giờ Tuning Fork bao bọc mã đếm giờ hiện có của các chương trình trình diễn tiêu chuẩn, vì vậy chúng ta có sự ăn khớp rất tốt giữa các việc đếm giờ đã có từ cả hai:

	AbsoluteTime startTime = clock.getTime();
               Code to be timed
      RelativeTime timeTaken = stopTime.subtract(startTime);

Để xây dựng và chạy mã này với các điểm theo vết Tuning Fork đã thêm vào, chúng ta chỉ cần bảo đảm đã thêm tệp tuningForkTraceGeneration.jar vào đường dẫn lớp (classpath).

Dữ liệu theo vết của Tuning Fork JVM

Cài đặt trình cắm thêm Eclipse cho WebSphere Real Time

Sau khi tải về Tuning Fork từ SourceForge (xem Tài nguyên) và cài đặt nó lên hệ thống của bạn:

  1. Khởi động trình hiển thị trực quan (visualizer).
  2. Chọn Help > Software Upgrades > Search for new features to install > Tuning Fork extensions for WebSphere Real Time > New Remote Site (Trợ giúp> Các bản nâng cấp phần mềm> Tìm kiếm các tính năng mới để cài đặt> Các phần mở rộng Tuning Fork cho WebSphere Real Time > Trang web từ xa mới).
  3. Nhập tên và địa chỉ URL http://awwebx04.alphaworks.ibm.com/
    ettktechnologies/updates
    .
  4. Khi Search Results (Các kết quả tìm kiếm) trả về một danh sách các tính năng để cài đặt, hãy mở rộng hộp WebSphere Real Time và chọn TuningFork Real Time JVM Feature 2.0.0.

Để cho phép ghi nhật ký dữ liệu JVM nội bộ, chúng ta thêm cờ -XXgc:perfTraceLog=filename.trace vào dòng lệnh.

Công cụ hiển thị trực quan Tuning Fork là một trình cắm thêm Eclipse có thể chạy trên Windows® hoặc Linux. Để bật cho phép các số liệu đã xây dựng sẵn cho IBM WebSphere Real Time JVM (Máy ảo Java Thời gian thực WebSphere của IBM), chúng ta cần thêm một trình cắm thêm riêng. (Cơ sở hạ tầng Tuning Fork là đa mục tiêu và cũng có thể được sử dụng với các ứng dụng Java, C, và C++ khác).

Khung nhìn có ích nhất trong Tuning Fork để hiểu việc thi hành chương trình chỉ đơn giản là một bức tranh về các sự kiện được hiển thị theo trình tự thời gian. Một số các hình vẽ được định nghĩa trước có sẵn để dùng với WebSphere Real Time (xem Tài nguyên). Các hình vẽ này cung cấp các khung nhìn kết hợp hữu ích của các dữ liệu JVM — ví dụ, Tóm tắt hiệu năng GC.

Chúng ta đã bổ sung thêm một công cụ đếm giờ Tuning Fork đơn giản vào ứng dụng trình diễn này. Mã này chỉ đơn giản định nghĩa một bộ đếm giờ và khởi động và dừng nó ngay lập tức trước và sau mã đếm giờ hiện có, vốn để kiểm tra các lần chạy vượt quá giờ của các luồng: 2 mili giây cho luồng Reader và 3 mili giây cho luồng Writer. Một khung nhìn Tuning Fork của một phần nhỏ dữ liệu này được hiển thị trong Hình 2, xác nhận mã đang chạy như đã định:

Hình 2. Theo vết của Tuning Fork — mã ứng dụng trình diễn
Theo vết của Tuning Fork - mã ứng dụng trình diễn

Hình 2 cho thấy rằng luồng Reader thi hành trong khoảng 130 micro giây và dữ liệu được gửi đến ổ cắm châm ngòi luồng Writer để chạy trong khoảng 900 micro giây (như mong đợi, vì luồng này có nhiều việc hơn để làm). Việc truyền toàn bộ dữ liệu, từ đọc nhiệt độ đến viết vào tệp, hoàn thành chỉ trong hơn 1 mili giây, hoàn toàn trong giới hạn của chúng ta là 5 mili giây. Chúng ta cũng có thể thấy rằng luồng Reader đang thức dậy đều đặn theo chu kỳ thời gian 2 mili giây của nó.

Trình hiển thị trực quan Tuning Fork tự động dóng khớp dấu thời gian từ hai nguồn dữ liệu, vì vậy trục thời gian X áp dụng cho cả hai luồng.

Điều gì sẽ xảy ra với mẫu hình này trong một chu kỳ GC? Không có Tuning Fork, tất cả những gì chúng ta có thể thấy là từ quan điểm của ứng dụng, nhưng bây giờ chúng ta có thể nhận thấy các khoảng tạm dừng GC ảnh hưởng đến ứng dụng của chúng ta như thế nào theo một cách rõ ràng hơn nhiều. Hình 3 cho thấy một khung nhìn của hoạt động GC trong Writer JVM (Máy ảo Java của Trình đọc):

Hình 3. Theo vết của Tuning Fork — các lát cắt GC
Theo vết của Tuning Fork - các lát cắt GC

Ở đây có thể nhận thấy ảnh hưởng của một GC trong suốt khoảng thời gian của luồng Writer. Mỗi mảnh công việc tăng thêm được thực hiện bởi Metronome (Máy nhịp) — một lượng tử (hoặc lát cắt trong Tuning Fork) — chạy trong khoảng 500 micro giây. Trong một lượng tử, tất cả các luồng ứng dụng trong Writer JVM đều bị tạm dừng, vì vậy thời gian thi hành (điển hình) tăng từ 900 micro giây lên đến 1,4 mili giây nếu lượng tử này xảy ra trong khi các luồng Writer đang chạy.

Lưu ý rằng tổng số nhiễu loạn đến luồng Writer sẽ hơi nhiều hơn 500 micro giây của lượng tử đó; các chi phí hoạt động chuyển đổi ngữ cảnh và các ảnh hưởng ô nhiễm tiềm năng từ bộ xử lý - bộ nhớ nhanh chắc chắn sẽ xảy ra. Nếu luồng Writer đã được gửi lên một lõi khác với lõi mà nó đã chạy vào lúc trước lượng tử GC, thì sẽ phải chịu trả giá cao hơn do bộ nhớ nhanh của lõi cụ thể.

Luồng Reader đã đang chạy trong một JVM riêng biệt và đã không bị ảnh hưởng bởi hoạt động GC trong thời gian chạy của luồng Writer vì máy tính đã có bốn lõi CPU đã chạy luồng GC của Writer JVM và luồng Reader đồng thời. (WebSphere Real Time sử dụng một luồng GC cho mỗi JVM theo mặc định).

Khi kiểm tra các chu kỳ GC khác trong Writer JVM, chúng ta có thể thấy một vài lần có sự kiện ngoại cảnh vượt quá 2 mili giây. Hãy xem xét chúng kỹ hơn giống như trong Hình 4, rõ ràng là chúng đã thật không may, bị ngắt bởi hai lượng tử GC:

Hình 4. Theo vết của Tuning Fork — hai lát cắt GC
Theo vết của Tuning Fork - hai lát cắt GC

Những trường hợp hai va chạm ấy là rất hiếm, nhưng chúng ta có thể tránh được chúng hoàn toàn không?

Để không bị hai lượng tử GC chạm vào, khoảng thời gian của mã ứng dụng cộng với một lượng tử GC cần phải lọt vào trong khoảng cách giữa hai lượng tử GC, mà thông thường là khoảng một mili giây. Vì vậy, luồng Writer sẽ cần phải giảm thời gian thi hành từ khoảng 900 micro giây xuống thấp hơn 500 micro giây. Tuy nhiên, ngay cả chiến lược này cũng sẽ không luôn luôn bảo đảm tránh va chạm với lượng tử GC, vì vài lý do:

  • Khả năng có biến đổi chút ít trong việc lập lịch trình khi nào tạm dừng để thu gom rác GC, do cách quản lý hợp đồng bảo đảm duy trì việc sử dụng 70% mutator (các luồng của ứng dụng).
  • Mỗi lõi của bộ xử lý thường có các luồng nhân mức ưu tiên cao được ràng buộc vào lõi đó để xử lý, ví dụ các ngắt và các bộ đếm giờ. Mặc dù các luồng này chỉ chạy với các khoảng thời gian rất ngắn, nhưng chúng có thể có ưu tiên cao hơn so với ứng dụng hoặc các luồng JVM và có thể gây nhiễu loạn giờ chạy các luồng ấy.
  • JVM có một luồng có ưu tiên cao hơn so với của người dùng hoặc các luồng GC khác — luồng cảnh báo GC, chạy mất một vài micro giây với mỗi 450 micro giây để quản lý các lát cắt thời gian GC. Nếu trình lập lịch biểu của hệ điều hành gửi nó lên cùng lõi như ứng dụng hay luồng GC của chúng ta, thì sẽ xảy ra độ trễ nhỏ.

Nghiên cứu việc thi hành ứng dụng ở mức micro giây bắt đầu để lộ ra (đôi khi hiếm thấy) các sự tương tác giữa các luồng, các lõi, và việc lập lịch biểu. Đôi khi có thể cần hiểu các sự tương tác này đầy đủ hơn bằng cách kết hợp dữ liệu của hệ điều hành; Tuning Fork cũng có thể nhập khẩu dữ liệu từ công cụ Linux System Tap, nhưng chúng tôi sẽ không trình bày công cụ đó trong bài viết này. IBM Toolkit for Data Collection (Bộ công cụ IBM để Thu thập dữ liệu) và Visual Analysis for Multi-Core Systems (Phân tích trực quan cho các hệ thống đa lõi) cũng có thể hiển thị trực quan dữ liệu này (xem Tài nguyên).

Thêm sự kiện ngoại cảnh của luồng Writer

Nếu bạn đã chạy ứng dụng trình diễn này, hẳn bạn sẽ có thể đã thấy một loạt các thông báo từ bàn giao tiếp của luồng Writer, ngay lập tức sau khi đã khởi động Reader JVM. Chúng ta sẽ bắt đầu với thông báo này:

Writer deadline missed after 0 good writes. Deadline was (3 ms, 0 ns), time taken was (48 
ms, 858000 ns)

Thông báo này nói rằng lần đầu tiên luồng Writer chạy, phải mất gần 49 mili giây để viết một bản ghi — lâu hơn nhiều so với lần chạy sau này, nó chỉ mất dưới 1 mili giây. Chúng ta biết rằng nguyên nhân của sự chậm trễ này không liên quan đến hoạt động JIT của việc chuyển đổi bytecode của phương thức này thành mã máy bản địa, vì chúng ta chạy với mã đã biên dịch-AOT còn JIT đã bị vô hiệu hóa trong thời gian chạy. Vùng nghi ngờ khác cần xem xét là việc nạp lớp, vì vấn đề này xảy ra lúc gọi ra lần đầu. Chúng ta có thể xác nhận điều này với Tuning Fork không? Hình 5 cho thấy một biểu đồ Tuning Fork của lần chạy đầu tiên của luồng Writer:

Hình 5. Theo vết của Tuning Fork — luồng Writer chạy và nạp lớp lần đầu tiên
Theo vết của Tuning Fork - luồng Writer chạy và nạp lớp lần đầu tiên

Như chúng ta đã nghi ngờ, chúng ta thấy hoạt động nạp lớp đáng kể ngay trước và trong thời gian lần chạy đầu tiên của luồng Writer, xác nhận rõ ràng rằng việc nạp lớp (và chạy khởi tạo lớp) là nguyên nhân của sự chậm trễ này. Những lần chạy tiếp theo của luồng Writer chỉ trong khoảng 1 mili giây hoặc thấp hơn.

Phần 2 của loạt bài này đã thảo luận về các kỹ thuật để tránh các độ trễ này. Một trong những cách khác mà các khung nhìn Tuning Fork có thể giúp chúng ta xác định các lớp cần nạp trước sẽ được hiển thị khi chúng ta sử dụng các hệ số khuyếch đại thời gian. Trong Hình 6, chúng ta thấy rằng org/apache/xerces/util/XMLChar chiếm nhiều hơn 3 mili giây để nạp:

Hình 6. Theo vết của Tuning Fork — xác định việc nạp lớp chậm
Theo vết của Tuning Fork - xác định việc nạp lớp chậm

Mặc dù ứng dụng của chúng ta khá đơn giản, việc sử dụng xử lý XML đòi hỏi phải nạp nhiều lớp hơn, vì vậy điều quan trọng là hoặc nạp trước chúng hoặc sử dụng một lần chạy vô công ban đầu trước khi ứng dụng bắt đầu vào giai đoạn trọng yếu về thời gian của nó.

Các sự kiện ngoại cảnh từ đầu đến cuối

Cho đến nay chúng ta đã nghiên cứu các sự kiện ngoại cảnh chỉ trong Writer JVM, nhưng yêu cầu của chúng ta là xử lý từ đầu đến cuối phải hoàn thành trong vòng 5 mili giây. Chúng ta không xem các bản ghi từ Reader JVM có các thời hạn cuối đã bị vượt qua, và thậm chí những lần đầu của nó chạy dưới 1 mili giây trước khi hạ xuống khoảng 140 micro giây. Việc bổ sung thêm công cụ cho Tuning Fork cũng cung cấp cho chúng ta các số liệu thống kê cho sự kiện trong Hình 7. (Sự kiện ngoại cảnh 3 mili giây đã xảy ra khi kết thúc JVM bằng Control-C, và sau đó việc nạp các lớp liên quan đến việc xử lý ngoại lệ đã xảy ra. Phần 2 đã thảo luận các vấn đề về các tuyến thi hành hiếm xảy ra này và các kỹ thuật để xác định và nạp trước các lớp cần thiết — chỉ chạy làm ấm trước thôi là chưa đủ).

Hình 7. Theo vết của Tuning Fork — các số liệu thống kê Reader
Theo vết của Tuning Fork - các số liệu thống kê Reader

Vấn đề là vào lúc khởi động Reader JVM, Writer JVM đã báo lại một lượng lớn các thời hạn cuối bị bỏ lỡ để truyền dữ liệu đầy đủ từ điểm đọc nhiệt kế trong Reader JVM đến khi viết nó vào Writer JVM:

Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(122 ms, 93000 ns)
Writer deadline missed after 0 good writes. Deadline was (3 ms, 0 ns), time taken was 
(48 ms, 858000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(122 ms, 517000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(121 ms, 567000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(120 ms, 541000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(119 ms, 525000 ns)

Mẫu hình này vẫn tiếp với việc giảm dần các lần chạy vượt thời hạn cho đến khi:

Data deadline missed after 0 good transfers. Deadline was 
(5 ms, 0 ns), transit time was (10 ms, 585000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(9 ms, 588000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(8 ms, 531000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(7 ms, 469000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(6 ms, 398000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was 
(5 ms, 518000 ns)
Writer deadline missed after 3087 good writes. Deadline was (3 ms, 0 ns), time taken was 
(3 ms, 316000 ns)

Vì vậy chúng ta có một độ trễ lớn ban đầu là 122 mili giây rồi giảm nhanh xuống, cuối cùng đạt tới một trạng thái mà chỉ thỉnh thoảng thời hạn cuối của luồng Writer hoặc luồng Reader bị bỏ lỡ. Hình 8 cho thấy một đồ thị thời gian truyền dữ liệu lúc khởi động:

Hình 8. Khởi động truyền-dữ liệu
Khởi động truyền-dữ liệu

Ngoài khoảng thời gian luồng Writer 48 mili giây đầu tiên, không có báo cáo nào về các lần chạy vượt quá từ cả luồng Reader lẫn luồng Writer trong suốt gần 120 lần đầu tiên truyền dữ liệu chậm — vậy thì độ trễ này ở đâu ra? Một lần nữa, các theo vết Tuning Fork của chúng ta có thể trợ giúp, lần này bằng cách kết hợp dữ liệu từ cả hai JVM và cả hai luồng ứng dụng, như hiển thị trong Hình 9. Chúng ta có thể chỉ rõ rằng không có hoạt động GC nào đã bắt đầu trong cả hai JVM bằng cách thêm -verbose:gc vào dòng lệnh, còn việc nạp lớp có thể chịu trách nhiệm một lần nữa không?

Hình 9. Theo vết của Tuning Fork — cả hai các JVM và các luồng ứng dụng
Theo vết của Tuning Fork - cả hai các JVM và các luồng ứng dụng

Trong Hình 9, việc nạp lớp trong Reader JVM là hàng thứ ba. Như dự kiến, nó đã hoàn thành bởi lần chạy đầu tiên của luồng Reader. Dòng dưới cùng là nạp lớp trong luồng Writer. Khi di con trỏ chuột trên các thanh trong phần giữa 20 và 70 mili giây trên trục X, chúng ta thấy rằng hầu như tất cả các thanh đều phải làm việc với XML. Một lần nữa, sự chậm trễ nạp lớp đang góp một phần đáng kể vào thời gian truyền dữ liệu của chúng ta. Độ trễ kéo dài nhất của chúng ta với 122 mili giây, là khoảng cách giữa thanh màu đỏ đầu tiên của bộ đếm giờ luồng Reader, và điểm cuối của thanh màu xanh lá cây đầu tiên của luồng Writer (có nhãn là 49,88 ms). Lần truyền dữ liệu thứ hai nhanh hơn một chút, và xu hướng này tiếp tục khi luồng Writer chạy hết công việc tồn đọng của các yêu cầu gửi đến, cho đến khi việc tồn đọng hết sạch và các lần truyền dữ liệu ở trong vòng 5 mili giây. Điều này giải thích mẫu hình về bỏ lỡ thời hạn cuối truyền dữ liệu lúc khởi động. Nhưng việc nạp lớp có phải là nhân tố duy nhất? Có thể việc truyền dữ liệu giữa Reader JVM và Writer JVM qua ổ cắm có góp phần không?

Độ trễ của ổ cắm

Việc sử dụng một ổ cắm để kết nối các JVM, cho phép ứng dụng được chia tách giữa hai máy tính, có thể đưa vào các chậm trễ. Chúng ta đã thực hiện các thay đổi sau cho cả hai mã ứng dụng của luồng Reader và luồng Writer để xem liệu có bất kỳ ảnh hưởng nào không:

  • Vô hiệu hóa Nagle: Thuật toán Nagle (xem Tài nguyên) nổi tiếng về gây ra sự chậm trễ trong hệ thống thời gian thực vì nó giữ trong vùng đệm các gói dữ liệu mạng trước khi gửi chúng. Có thể kiểm tra việc thiết lập Nagle bằng các ứng dụng Java với socket.getTcpNoDelay() và, nếu thấy Nagle được bật lên, thì có thể tắt nó bằng cách thiết lập setTcpNoDelay(true). Tuy nhiên, việc vô hiệu hóa Nagle cũng không ảnh hưởng đến các việc truyền dữ liệu khởi động chậm.
  • Sử dụng PerformancePreferences: Có thể sửa đổi ổ cắm mặc định để có hành vi gần hơn với các yêu cầu của chúng ta, bằng cách sử dụng setPerformancePreferences(1, 2, 0). Điều này gán tầm quan trọng cao nhất với độ trễ thấp, theo sau là thời gian kết nối ngắn và tầm quan trọng thấp nhất dành cho băng thông cao. Việc thêm điều này vào mã trình diễn làm giảm đáng kể độ trễ khởi động — xem Hình 10:
    Hình 10. Theo vết của Tuning Fork — với ổ cắm PerformancePreferences
    Theo vết của Tuning Fork - với ổ cắm PerformancePreferences
    Bây giờ độ trễ chính đã giảm còn khoảng 40 mili giây do việc nạp lớp gây ra, và có thể loại bỏ nó bằng cách nạp trước các lớp đó.

Kết luận

Bài viết này kết thúc loạt bài Phát triển với Java thời gian thực. Như toàn bộ loạt bài này đã nhấn mạnh, khả năng dự báo là ưu tiên hàng đầu cho một ứng dụng thời gian thực. Trong bài viết này, chúng ta đã thiết kế và viết một ứng dụng Java thời gian thực đơn giản và chỉ rõ cách sử dụng kết hợp các bộ đếm giờ và các công cụ để phân tích việc thi hành ứng dụng và xác nhận nó thực hiện theo dự báo như thế nào. Đồng thời, chúng ta đã giải thích sự tương tác giữa các khoảng tạm dừng GC và một ứng dụng đang chạy, cho thấy việc nạp lớp có thể gây ra các chậm trễ như thế nào, và đã quan sát ảnh hưởng của việc sử dụng XML trong một ứng dụng đơn giản. Và chúng ta đã xác định được tầm quan trọng của việc phân tích xuyên suốt từ đầu đến cuối của các hệ thống được kết nối và đã phát hiện và làm giảm thời gian trễ gây ra bởi các ổ cắm mạng.

Dữ liệu hiệu năng mà chúng ta đã nói trong bài viết này đã được xác định trong một môi trường có kiểm soát. Các kết quả thu được trong các môi trường hệ điều hành khác có thể thay đổi đáng kể, vì vậy bạn nên kiểm tra các dữ liệu áp dụng được cho môi trường cụ thể của bạn. Cũng lưu ý rằng các ứng dụng phân tán về vật lý chịu ảnh hưởng của một phạm vi lớn các nguồn gốc biến động (xem Tài nguyên). Chúng ta đã giảm hoặc tránh được một số trong đó bằng cách chạy ứng dụng trình diễn trên một máy tính duy nhất. Bạn có thể xử lý một số các tham số điều chỉnh mạng Java mà chúng tôi đã trình bày như là các gợi ý.


Tải về

Mô tảTênKích thước
Source codej-rtjdev3.tar.gz5KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java
ArticleID=777384
ArticleTitle=Phát triển với Java thời gian thực, Phần 3: Viết, xác nhận hợp lệ và phân tích một ứng dụng Java thời gian thực
publish-date=11302011