Mô hình bộ nhớ J2ObjC

Tài liệu này mô tả cách quản lý bộ nhớ theo mã dịch J2ObjC và cách các chương trình hoạt động khi truy cập vào bộ nhớ dùng chung.

Quản lý bộ nhớ

Một trong những mục tiêu của J2ObjC là tạo ra bản dịch mã sẽ tích hợp liền mạch vào môi trường đếm tham chiếu của Objective-C. Điều này giúp bạn dễ dàng sử dụng mã Java đã dịch từ Objective-C được viết sẵn vì không có vấn đề gì về việc chuyển quyền sở hữu đối với các đối tượng được truyền giữa môi trường Java và Objective-C.

Vì Java sử dụng tính năng thu gom rác để quản lý bộ nhớ, nên mã Java không chứa hoạt động quản lý bộ nhớ rõ ràng cho các đối tượng. Do đó, J2ObjC phải chèn các lệnh gọi đếm tham chiếu một cách thích hợp để đảm bảo rằng các đối tượng được giải phóng vào đúng thời điểm. Chúng tôi đã thoả thuận với bộ quy tắc sau đây mà chúng tôi nhận thấy là vừa hiệu quả, vừa thiết thực:

  • Tất cả đối tượng sẽ tồn tại trong ít nhất khoảng thời gian của nhóm tự động phát hành hiện tại.
    • Quy tắc chung này cho phép chúng ta bỏ qua nhiều lượt giữ lại và phát hành vốn cần thiết.
  • Các biến cục bộ không được giữ lại.
    • Không tham chiếu đến lệnh gọi khi đọc hoặc ghi các biến cục bộ.
  • Các trường sẽ được giữ lại.
    • Việc chỉ định lệnh gọi trường sẽ giữ lại giá trị mới và tự động phát hành dựa trên giá trị cũ.
  • Các đối tượng mới sẽ được tự động phát hành ngay lập tức. (trừ phi được gán ngay cho một trường)

Chu kỳ tham chiếu

Chu kỳ tham chiếu tồn tại khi một đối tượng tham chiếu đến chính nó trực tiếp hoặc gián tiếp thông qua các trường của đối tượng đó. Chu kỳ tham chiếu có thể được dọn dẹp bằng cách thu gom rác của Java, nhưng sẽ rò rỉ bộ nhớ trong môi trường đếm tham chiếu của Objective-C. Không có cách tự động nào để ngăn chu kỳ tham chiếu xảy ra; tuy nhiên, chúng tôi cung cấp công cụ Trình tìm chu kỳ giúp tự động phát hiện chu kỳ. Dưới đây là một số cách phổ biến để khắc phục chu kỳ tham chiếu:

  • Thêm chú thích @weak hoặc @weakOuter để làm suy yếu một trong các tệp tham chiếu.
  • Thêm phương thức cleanup() vào một trong các đối tượng sẽ đặt một số trường thành rỗng. Hãy gọi cleanup() trước khi loại bỏ đối tượng.
  • Thiết kế lại mã để tránh tạo ra một chu kỳ tham chiếu hoàn toàn.

Bộ nhớ dùng chung

Trong một chương trình đa luồng, nhiều luồng có thể chia sẻ một số dữ liệu. Java cung cấp một số công cụ để cho phép truy cập an toàn cho luồng vào dữ liệu được chia sẻ. Phần này mô tả tính năng hỗ trợ của J2ObjC để truy cập vào dữ liệu dùng chung.

Đã đồng bộ hoá

J2ObjC liên kết từ khoá synchronized trực tiếp với Objective-C @synchronized.

Nguyên tử

Java đảm bảo tính nguyên thức cho các lượt tải và lưu trữ mọi kiểu, ngoại trừ longdouble. Xem JLS-17.7. Ngoại trừ các trường volatile (mô tả dưới đây), J2ObjC không cung cấp cách xử lý đặc biệt nào để đảm bảo tải và lưu trữ nguyên tử. Điều này có nghĩa như sau:

  • Vì tất cả các nền tảng iOS đều là 32 hoặc 64 bit, nên tải và lưu trữ các loại nguyên hàm ngoại trừ longdouble là nguyên tử trên thiết bị 32 bit và tất cả đều là nguyên tử trên hệ thống 64 bit.
  • Tải và lưu trữ các loại đối tượng không phải là nguyên tử trong J2ObjC.
    • Việc cập nhật nguyên tử số lượng tệp đối chiếu quá tốn kém.
    • Bạn có thể tạo một trường đối tượng ở dạng nguyên tử bằng cách khai báo trường đó volatile. (xem bên dưới)

Các trường dễ biến động

Đối với các trường volatile, Java cung cấp cả hệ số nguyên tố và thứ tự nhất quán theo trình tự (JLS-8.3.1.4), có thể dùng để đồng bộ hoá. J2ObjC đảm bảo tương tự như Java cho tất cả các trường volatile. J2ObjC sử dụng các cơ chế sau cho các trường volatile:

  • Các loại nguyên tố được ánh xạ tới các loại nguyên tử c11.
    • Ví dụ: volatile int -> _Atomic(jint)
  • Các trường đối tượng được bảo vệ bằng khoá mutex pthread. (không thể sử dụng khoá xoay do chế độ đảo ngược ưu tiên)
    • Cần loại trừ lẫn nhau để ngăn chặn điều kiện tranh đấu bằng cách đếm tệp đối chiếu.
    • Cách triển khai rất giống với thuộc tính nguyên tử của Objective-C.

Loại nguyên tử

Java cung cấp một số loại nguyên tử trong gói java.util.concurrent.atomic. Tất cả đều được hỗ trợ đầy đủ trong J2ObjC với các phương thức triển khai tuỳ chỉnh.

Trường cuối cùng

Java đảm bảo rằng một luồng sẽ thấy các giá trị đã khởi tạo cho các trường cuối cùng của một đối tượng mà không yêu cầu đồng bộ hoá khi chia sẻ đối tượng. (JSL-17.5) Tuy nhiên, vì J2ObjC không hỗ trợ quyền truy cập nguyên tử vào các loại đối tượng không thay đổi (xem ở trên), nên không có cách nào an toàn để chia sẻ một đối tượng mà không cần đồng bộ hoá. Do đó, người dùng J2ObjC không có thêm điều kiện ràng buộc nào bằng cách bỏ qua các hàng rào bộ nhớ cần thiết cho các trường cuối cùng.