作者jtmh (:))
看板java
标题Re: [问题] null 和 GC 与 LinkedList 的原始码
时间Thu Oct 20 10:35:34 2011
※ 引述《s3748679 (冷羽翼尘)》之铭言:
...
: 在这边小弟有个疑问,如果这边的原始码改成了这样:
: private E remove(Entry<E> e) {
: if (e == header)
: throw new NoSuchElementException();
假设目前 linked list 中包括 header 的话只有两个 entries (即 size 为 1),
则记忆体中变数所占的空间及彼此间的关联性会如下图所示:
(请按空白或 PageDown 键翻页 >>)
header ┌─┐ e ┌─┐
│ ┼┬→ element ┌───┐ │ │
└─┘│ │ null │ └┼┘
│ next ├───┤ │
│ │ ┼─┐ ↓
│ previous ├───┤ ├→ element ┌───┐ ┌───┐
│ │ ┼─┘ │ ┼→│ ... │
│ └───┘ next ├───┤ └───┘
│ │ ┼─┐
│ previous ├───┤ ├─┐
│ │ ┼─┘ │
│ └───┘ │
│ │
└───────────────────────────┘
: E result = e.element;
: e.previous.next = e.next;
: e.next.previous = e.previous;
在执行完上面三行後则会变成:
(请按空白或 PageDown 键翻页 >>)
header ┌─┐ e ┌─┐
result ┌─┐
│ ┼
┬→ element ┌───┐ │ │
│ │
└─┘
│ │ null │ └┼┘
└┼┘
│ next ├───┤ │
│
│ │
┼─┐ ↓
↓
│ previous ├───┤
├┐ element ┌───┐ ┌───┐
│ │
┼─┘│ │ ┼→│ ... │
│ └───┘
│ next ├───┤ └───┘
│ │ │ ┼─┐
│ │ previous ├───┤ ├─┐
│ │ │ ┼─┘ │
│ │ └───┘ │
│ │ │
└─────────────┴─────────────┘
: // 把这二行砍了
: // e.next = e.previous = null;
: // e.element = null;
如果上面两行有执行的话就变成:
(请按空白或 PageDown 键翻页 >>)
header ┌─┐ e ┌─┐
result ┌─┐
│ ┼
┬→ element ┌───┐ │ │
│ │
└─┘
│ │ null │ └┼┘
└┼┘
│ next ├───┤ │
│
│ │
┼─┐ ↓
↓
│ previous ├───┤
├┐ element ┌───┐ ┌───┐
│ │
┼─┘│ │ null │ │ ... │
│ └───┘
│ next ├───┤ └───┘
│ │ │ null │
│ │ previous ├───┤
│ │ │ null │
│ │ └───┘
│ │
└─────────────┘
那麽在离开 remove(Entry<E> e) 这个方法後,
因为 e 和 result 变数也跟着消失,
上图中
深灰色部分的空间即可顺利回收。
而原本 result 所指向的那块空间,
若 result 值回传後未被使用的话也可顺利回收。
(请按空白或 PageDown 键翻页 >>)
那麽如果那两行没执行呢?
从第二张图的
深灰色部分可知,
在离开 remove(Entry<E> e) 这个方法後,
e 和 result 变数也跟着消失,
而原本 result 所指向的那块空间,
就算是 result 值回传後未被使用的话,
因为仍有
深灰色的
element 指向它,
所以 GC 会无法回收它。
那
深灰色部分的空间能被顺利回收吗?
从这个 size 为 1 的例子来看是可以的。
但想想 size 在 2 以上的情形,
每当 remove 掉一个 entry 时,
该 entry 的
next 和
previous 都还继续指着原本在它前、後的两个 entries,
那在它所指的前或後的 entry 被 remove 後,
假设这时它尚未被回收的话,
GC 便会因为它还指着前或後的 entry 而无法回收该 (前或後的那个) entry 了。
: size--;
: modCount++;
: return result;
: }
: public void clear() {
: // 把这里也砍了
: //Entry<E> e = header.next;
: //while (e != header) {
: // Entry<E> next = e.next;
: // e.next = e.previous = null;
: // e.element = null;
: // e = next;
: //}
: header.next = header.previous = header;
: size = 0;
: modCount++;
: }
这里的情况和上面讲的相同,
除非 GC 刚好依照原本 linked list 的顺序去检查能否回收,
不然一样会遇到上述的问题。
: 这样的话GC还回收得到中间的Entry吗? 不晓得会不会造成记忆体遗失的问题@@"
: 还是说这样做GC还是回收得到,只是会比较慢!? (猜测)
: PS: 看过了wiki,但我有看没有懂:
: 虽然记忆体管理器可以回复不能存取的记忆体,但它不可以释放可存取的记忆体因为仍有
: 可能需要使用。现代的记忆体管理器因此为程式设计员提供技术来标示记忆体的可用性,
: 以不同级别的「存取性」表示。记忆体管理器不会把需要存取可能较高的物件释放。当物
: 件直接和一个强参照相关或者间接和一组强参照相关表示该物件存取性较强。(强参照相
: 对於弱参照,是防止物件被回收的一个参照。)要防止此类记忆体泄漏,开发者必须使用
: 物件後清理参照,一般都是在不再需要时将参照设成null,如果有可能,把维持强参照的
: 事件侦听器全部登出。
Java 中的参照变数预设都是握有对物件的强参照,
本例中的 header, element, next, previous, e 均为强参照变数,
当物件有被任何强参照变数指到时就无法被 GC 回收。
至於弱参照的话我记得 Java 也有相关的机制可用,
详情可参看 java.lang.ref package。
: wiki 内部记忆体泄漏
: 网址: http://zh.wikipedia.org/zh-tw/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F
: 希望各位大大能对半路出家的我指点迷津... Thanks~
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 218.175.149.42
1F:→ jtmh:最後一段中 element 和 result 要视其型别而定,若为基本资料 10/20 10:50
2F:→ jtmh:型别,则 element 和 result 并不是参照变数;若是参照资料型 10/20 10:52
3F:→ jtmh:别的话,则 element 和 result 也都属强参照变数。 10/20 10:54
4F:推 PsMonkey:这篇也太有爱了吧? (y) 10/20 12:31
5F:→ s3748679:感谢jtmh回文=ˇ=,因为还有些问题,正在编辑回文中~ 10/20 22:04
6F:推 s3748679:啊对~ 前文章我应该把原始码摆出来的.. 10/20 23:14