Hỏi về Garbage collection

  • mình đang thắc mắc rằng java đã có garbage collection thì tại sao còn bị out of memory được nhỉ, và vấn đề là khi nào GB chạy (nếu như mình không trực tiếp call nó)
  • giả sử, nếu khởi tạo obj trong loop thì sau mỗi vòng lặp obj đó không được refer đến nữa, vậy khi nào GB sẽ dọn nó
1 Like

Hóng cao nhân vì mình cũng có thắc mắc tương tự XD

2 Likes

đang ngồi trà đá, ông anh tự dưng hỏi về mấy cái core này mà khoai phết

3 Likes

đọc rồi ông ơi, ko tìm được cái cần tìm :stuck_out_tongue::stuck_out_tongue::stuck_out_tongue:

https://docs.oracle.com/en/java/javase/12/gctuning/introduction-garbage-collection-tuning.html#GUID-326EB4CF-8C8C-4267-8355-21AB04F0D304
ông ngủ ngon tui đi ngủ phát

2 Likes

Khi obj A trỏ đến obj B là obj B cũng trỏ đến A thì GC ko thể dọn memory đc code vd: (ví dụ giải thích cả 3 câu hỏi của bạn) https://ktmt.github.io/blog/2013/06/02/weakhashmap-va-weak-reference-trong-java/

jvm tự biết khi nào hệ thống cần memory thì nó sẽ tìm xem obj của thằng nào ko dùng nữa để dọn, sài 4 dạng tham chiếu trong bài viết để đánh dấu cho gc lúc nào nên dọn obj đó.

3 Likes

Với vấn đề 1 thì dễ giải thích thôi. GC chỉ là bộ dọn rác cho bạn. Không phải thần thánh tăng thêm RAM cho bạn. Bạn vẫn không thể nhét 1 con voi vào 1 cái hộp nhỏ dù thằng GC có thông minh đến mức nào đi chăng nữa. Ngoài ra outofmem có thể xảy ra khi ứng dụng cần 1 lượng bộ nhớ lớn tại 1 thời điểm cũng có thể khiến bộ nhớ không kịp giải phóng để dành chỗ cho yêu cầu bộ nhớ từ ứng dụng.
Quá trình GC không diễn ra ngay lập tức, quá trình này cũng tốn tài nguyên nên sẽ được chạy vào thời điểm thích hợp nhất. Do đó 1 object vẫn còn tồn tại ngay cả khi không còn bất kì tham chiếu nào đến nó cả.

9 Likes

GC sẽ chỉ dọn những gì mà nó được phép, như là các đối tượng không còn tham chiếu đến.
Mình không sâu về Java nhưng được biết GC của Java hoạt động tương tự như JavaScript (JS)
Và như chương trình JS như bên dưới có tạo hai đối tượng Array.
Ở dòng code thứ 2, đối tượng [1, 2] đã không còn biến trỏ đến nó nữa và sẽ được GC xóa trong bộ nhớ.

var a = [1, 2];
a = [3, 4];

Tuy nhiên nếu chương trình bạn sử dụng nhiều bộ nhớ mà không đủ điều kiện cho phép GC dọn dẹp thì sẽ bị out of memory.

Khi nào GC dọn dẹp thì ta không thể biết. Cái này là do team phát triển JVM sẽ đưa ra thuật toán hợp lý nhất theo ý của họ.
Vì dọn dẹp liên tục thì sạch bộ nhớ, nhưng mệt CPU giảm performance, và ngược lại.

5 Likes

mình ko rõ lắm. nhưng call system.gc() vẫn đc mà

system.gc() chỉ là lời gọi ép GC phải hoạt động sớm nhất có thể. Còn khi nào chạy thì vẫn không kiếm soát được.

5 Likes

Chế ra gc để đỡ phải quản lý thủ công như C/C++ mà.

2 Likes

Khi một vùng nhớ được tạo, sẽ có một con trỏ trỏ đến vùng nhớ đó. Vùng nhớ được lưu ở heap, con trỏ được lưu ở stack. Như vậy heap lưu data, stack lưu context.
Khi hàm return, thì các con trỏ được khởi tạo sẽ được bốc ra khỏi memory (nếu bạn từng viết code ASM thì sẽ hiểu cái này). Đây có lẽ là thời điểm thích hợp để GC thực thi.
Nếu bằng một cách thần kỳ nào đó, bạn viết code mà sau khi thực thi lệnh, vùng nhớ được khai báo vẫn còn được trỏ tới mặc dù không sử dụng, thì GC không biết để dọn dẹp vùng nhớ này.
Ví dụ, bạn implement một ArrayList có khả năng tăng capacity tự động:

ArrayList implements List {
 int capacity = 2;
 int size = 2;

 public void increaseCapacity() {
   this.capacity *= 2;
   // code tạo vùng nhớ mới với capacity x2 rồi copy các phần tử cũ qua
 }
 }

Nhưng khi lúc implement hàm pop(), bạn chỉ thực hiện như sau:

 public pop() {
    this.size--;
}

Ở đây, các phần tử không sử dụng đến sau hàm pop() vẫn được trỏ đến. Vì vậy, GC không thể dọn dẹp các vùng nhớ này được. Giả sử List của bạn lưu data int. Con số data tăng lên sẽ theo cấp
số nhân (4bytes): 2, 4, 8, 16, …
Sau khoảng 27 lần increaseCapacity, chương trình của bạn sẽ chiếm 1GB RAM mà GC không dọn dẹp được.

Ý tưởng là sẽ dọn dẹp sau mỗi method. Còn chi tiết thì bạn có thể xem trong spec của JVM. Hóng share :smiley:

5 Likes

Out of Memory là lỗi hết phần nhớ.
Ví dụ thiết bị Android bộ nhớ bị giới hạn, mà muốn xin 1TB để lưu ảnh thì “Out of Memory” là cái chắc.
Câu hỏi hợp lý phải là “Java có garbage collector nhưng vẫn bị memory leak”.

Lý do thì là do garbage collector chỉ dọn những cái không còn tham chiếu. Những cái nào mà còn tham chiếu thì không bị collected. Nhưng tại sao còn tham chiếu mà lại coi là leak? Tại vì cái đó ở trạng thái không sống nữa.

Ví dụ: Trong 1 ứng dụng Android, 1 object Activity chạy một ASyncTask, object ASyncTask này giữ 1 tham chiếu đến object Activity. ASyncTask sẽ chạy ở 1 thread khác, giả sử chạy rất lâu. Còn object Activity thì bất ngờ bị Android OS hủy đi, bị destroyed chả hạn, object Activity không còn sống nữa, nhưng garbage collector không thể collect object Activity (vì object ASyncTask vẫn đang chạy) => memory leak.

4 Likes

Theo mình có 2 vấn đề:

  1. Bạn cấp phát đối tượng quá lớn, vượt quá số lượng bộ nhớ hệ thống hoặc bộ nhớ mà java có thể cấp phát được cho bạn
  2. Có nhiều đối tượng mà GB không quản lý được (unmanaged resource) nên mỗi lần bạn gọi đối tượng mới mà không giải phóng nó thì bộ nhớ sẽ tích dần đến lúc nào đấy ứng dụng của bạn tiêu tốn quá nhiều bộ nhớ
    Còn GB nó sẽ chạy song song với ứng dụng của bạn, nó sẽ tự động thu hồi lại bộ nhớ của những đối tượng (mamaged resource). Ví dụ phòng học của bạn bầy rất nhiều thứ, giấy nháp, vỏ kẹo, … . Bạn chỉ cần nháp xong vất xuống hoặc ăn kẹo xong vất ra nhà thì ngay sau đó hoặc trễ hơn tý là có người vào quét dọn phòng cho bạn (bạn chỉ mất vỏ kẹo chứ không mất iphone được)
3 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?