[問題] 關於PhantomReference

作者: NullLife (廢材大叔有點累)   2020-07-12 14:54:02
小弟我最近在研究SoftReference、WeakReference、PhantomReference等功能
SoftReference、WeakReference很好理解, 沒什麼問題.
但我在研究PhantomReference時覺得我的code跟我對PhantomReference理解有點出入
以下是我測試的code, jvm 設定 -Xms5M -Xmx10M (有透過MBean確認過參數無誤)
(01) private volatile boolean isRun = true;
(02)
(03) private void test() throws InterruptedException {
(04) ReferenceQueue<byte[]> rq = new ReferenceQueue<>();
(05)
(06) pollingReferenceQueue(rq); // fork thread poll referenceQueue
(07)
(08) try {
(09) List<Object> l = new ArrayList<>();
(10)
(11) int times = 20;
(12) long restMs = 1000;
(13) for (int i = 1; i <= times; i++) {
(14) byte[] buff = new byte[1024 * 1024];
(15) PhantomReference<byte[]> pr = new PhantomReference<>(buff, rq);
(16) l.add(pr); // 這邊打開讓pr被強引用, rq才會收到資料??
(17)
(18) System.gc();
(19)
(20) System.out.println("<main> " + "(" + i + ") " + pr);
(21)
(22) Thread.sleep(restMs);
(23) }
(24)
(25) } finally {
(26) isRun = false;
(27) }
(28) }
(29)
(30) private void pollingReferenceQueue(ReferenceQueue<byte[]> rq) {
(31) new Thread(() -> {
(32) while (this.isRun) {
(33) Reference<? extends byte[]> v = rq.poll();
(34)
(35) if (v == null) {
(36) System.out.println("<poll> " + null);
(37) } else {
(38) System.out.println("<poll> recycle " + v.toString());
(39) }
(40)
(41) try {
(42) Thread.sleep(100);
(43) } catch (InterruptedException e) {
(44) e.printStackTrace();
(45) }
(46) }
(47) }, "polling").start();
(48) }
我的問題在第16行, 當沒有把pr給強引用的時候, 當要被GC的時候並不會丟到rq裡?
但當pr給強引用的時候, 要被回收時就會出現在rq裡?
我對ReferenceQueue的理解是
"當Reference所參照的物件準備要回收的時候, 其Reference會先放置ReferenceQueue"
"因此可以利用ReferenceQueue得知有某些物件沒有人使用準備要被回收了!"
所以我不能理解的是為什麼pr被強引用之後準備回收才會被丟到rq裡?
但pr沒有被強引用要被回收時卻沒有丟到rq裡? (因為沒有OOM)
不曉得有沒有大大能解惑, 是我理解錯誤還是我的code在使用上就錯了?
作者: bitlife (BIT一生)   2020-07-13 09:22:00
pr在迴圈內,每繞一次就生命週期結束,list救了它.把pr移出迴圈再試看看
作者: tw11509 (John-117)   2020-07-13 11:23:00
https://i.imgur.com/zSwQauH.jpg 我看文件上面寫的,感覺跟這個有關https://bit.ly/3foeCVf 提供一下文件連結
作者: bitlife (BIT一生)   2020-07-16 19:52:00
今天發現我第一行寫的不夠清楚,應該是把pr的宣告移出迴圈,看到原po有回覆才發現當初寫得太過簡略 XD
作者: tw11509 (John-117)   2020-07-16 20:22:00
B大的答案我有試過,看起來還是一樣,看來就像文件所說只要pr變成unreachable,就不會被加入到queue中
作者: bitlife (BIT一生)   2020-07-17 09:30:00
感覺是如果不用list來記錄每次new的PhantomReference,只會記錄到最後一次的new PhantomReference(.),而那次迴圈會結束,list l 和pr的生命週期也結束,而gc只是標誌將執行並非呼叫當下立即執行,因此最後一次gc實際執行時,最後一次的new PhantomReference也unreachable了.所以l的效果是讓前面幾次的gc來得及回報前幾次的pr. 要確保gc實際執行時pr還活著,光移出迴圈還不夠,可能要變field,而且應該用array或list,以記錄每次的new PhantomReference(.)
作者: tw11509 (John-117)   2020-07-17 16:00:00
我的看法大致跟B大一樣,重點就是確保pr在GC時還可以被reach到就可以了

Links booklink

Contact Us: admin [ a t ] ucptt.com