2010-07-29 6 views
6

Tôi đã đọc these slides về trình hoàn thiện Java. Trong đó, tác giả mô tả một kịch bản (trên trang 33), theo đó CleanResource.finalize() có thể được chạy bởi luồng kết thúc trong khi CleanResource.doSomething() vẫn đang chạy trên một chuỗi khác. Làm sao điều này xảy ra được?Java GC Câu hỏi: Làm cách nào một đối tượng trở nên không thể truy cập được trong khi một trong các phương pháp của nó vẫn đang được thực hiện?

Nếu doSomething() là phương pháp không tĩnh, sau đó thực thi phương pháp đó, một nơi nào đó phải có tham chiếu mạnh mẽ đến nó ... phải không? Vậy làm cách nào để tham chiếu này có thể bị xóa trước khi phương thức trả về? Có thể một thread khác swoop trong và null ra tham chiếu đó? Nếu điều đó xảy ra, liệu doSomething() vẫn trở lại bình thường trên chuỗi ban đầu?

Đó là tất cả tôi thực sự muốn biết, nhưng đối với một thực sự trên và vượt ra ngoài câu trả lời, bạn có thể cho tôi biết tại sao doSomething() trên slide 38 là tốt hơn so với doSomething() trên slide 29. Tại sao nó đủ để đơn giản gọi phương thức keepAlive() này? Bạn có cần phải bọc toàn bộ cuộc gọi đến myImpl.doSomething() trong khối synchronized(this){} không?

Trả lời

3

EDIT3:

Kết quả cuối cùng là trình hoàn chỉnh và phương pháp thông thường có thể được thực hiện đồng thời trên cùng một trường hợp. Dưới đây là giải thích về cách điều đó có thể xảy ra. Mã này về cơ bản là:

class CleanResource { 
    int myIndex; 
    static ArrayList<ResourceImpl> all; 

    void doSomething() { 
    ResourceImpl impl = all.get(myIndex); 
    impl.doSomething(); 
    } 

    protected void finalize() { ... } 
} 

Với mã này khách hàng:

CleanResource resource = new CleanResource(...); 
resource.doSomething(); 
resource = null; 

Điều này có thể được JITed một cái gì đó giống như giả này C

register CleanResource* res = ...; call ctor etc.. 
// inline CleanResource.doSomething() 
register int myIndex = res->MyIndex; 
ResourceImpl* impl = all->get(myInddex); 
impl->DoSomething(); 
// end of inline CleanResource.doSomething() 
res = null; 

đã thi như thế, res sẽ bị xóa sau khi inline CleanResource.doSomething() được thực hiện, do đó, gc sẽ không xảy ra cho đến sau khi phương thức đó đã kết thúc thực hiện. Không có khả năng hoàn thành việc thực thi đồng thời với một phương thức thể hiện khác trên cùng một cá thể.

Tuy nhiên, ghi vào res không được sử dụng sau thời điểm đó, và cho rằng không có hàng rào, nó có thể được di chuyển trước đó trong việc thực hiện, ngay lập tức sau khi ghi:

register CleanResource* res = ...; call ctor etc.. 
// inline CleanResource->doSomething() 
register int myIndex = res->MyIndex; 
res = null; /// <----- 
ResourceImpl* impl = all->get(myInddex); 
impl.DoSomething(); 
// end of inline CleanResource.doSomething() 

Tại đánh dấu location (< ---), không có tham chiếu đến cá thể CleanResource, và do đó nó đủ điều kiện để thu thập và phương thức finalizer được gọi. Kể từ khi finalizer có thể được gọi là bất kỳ thời gian sau khi tham khảo cuối cùng được xóa, nó có thể cho finalizer và phần còn lại của CleanResource.doSomething() để thực hiện song song.

EDIT2: keepAlive() đảm bảo rằng con trỏ this được truy cập ở cuối phương thức, để trình biên dịch không thể tối ưu hóa việc sử dụng con trỏ. Và đó truy cập này được đảm bảo để xảy ra theo trình tự quy định (từ đồng bộ đánh dấu một hàng rào không cho phép tái đặt hàng của đọc và viết trước/sau thời điểm đó.)

Original Post:

Ví dụ đang nói phương thức doSomething được gọi, và một khi được gọi, dữ liệu được tham chiếu qua con trỏ this có thể được đọc sớm (myIndex trong ví dụ).Khi dữ liệu được tham chiếu được đọc, con trỏ this không còn cần thiết trong phương thức đó nữa và cpu/trình biên dịch có thể ghi đè lên thanh ghi/khai báo đối tượng không còn có thể truy cập nữa. Vì vậy, GC có thể đồng thời gọi trình finalizer cùng lúc với phương thức doSomething() của đối tượng đang chạy.

Nhưng vì con trỏ this không được sử dụng, thật khó để biết cách này sẽ có hiệu ứng hữu hình như thế nào.

EDIT: Vâng, có lẽ nếu có được lưu trữ con trỏ tới các lĩnh vực của đối tượng đang được truy cập thông qua bộ nhớ cache, tính từ this trước khi nó được khai hoang, sau đó các đối tượng được khai hoang các tài liệu tham khảo bộ nhớ trở nên không hợp lệ. Có một phần trong tôi khó tin rằng điều này là có thể, nhưng sau đó một lần nữa, điều này có vẻ là một trường hợp góc khó khăn, và tôi không nghĩ có bất cứ điều gì trong JSR-133 để ngăn chặn điều này xảy ra theo mặc định. Đó là một câu hỏi liệu một đối tượng được coi là chỉ được tham chiếu bởi con trỏ tới cơ sở của nó hay bằng con trỏ tới các trường của nó.

+0

Vậy điều này có thể xảy ra trên máy ảo dựa trên ngăn xếp không? Bây giờ tôi đang suy nghĩ ở cấp độ bytecode như bạn là tôi hiểu điều này tốt hơn nhiều. Tôi đoán trong trường hợp của một máy stack tài liệu tham khảo không cần phải được trên ngăn xếp cho 'invokevirtual' để hoàn thành? Vì vậy, một khi giá trị 'myIndex' được lấy ra thì' this' có thể được bật ra khỏi ngăn xếp và có khả năng khai hoang? –

+0

Trên một triển khai dựa trên ngăn xếp nghiêm ngặt, thì điều này là không thể, vì con trỏ này sẽ vẫn còn trên ngăn xếp. Nhưng với phương thức nội tuyến, không thực hiện lệnh và đăng ký phân bổ, cuộc gọi đồng thời tới trình kết thúc sẽ trở thành có thể. Xem bản chỉnh sửa mới nhất của tôi trong saga tiếp tục này. :) – mdma

+0

Câu trả lời hay! Tôi thậm chí không biết ai khác ngoài VM Dalvik của Android đã triển khai JVM dựa trên đăng ký. Điều này có phổ biến không? –