Q1:为什么程序的数据需要放在堆、栈两个不同(甚至更多)的地方?
对于堆和栈中的数据内容来说:
栈:编译器需知道数据内容的生存周期、但是可以快速管理和分配栈内存;
堆:编译器无需知道数据内容的生存周期,保证灵活性、但是分配和回收内存不如把数据放在栈中来得快;
Q2:对象在其生命周期结束后经历什么步骤而后被释放?销毁机制具体是怎样的?底层通过什么实现?
当程序执行到一个块or作用域(scope)的结尾,会自动清理其维护的栈中的内存数据。
于是,如果保存在栈中的唯一reference挂掉了,就意味着再没有办法可以操作其原先引用的对象了。
但是保存在堆中的对象在这时候还没有被清理掉。
对于在堆中,没有被引用的对象。垃圾回收器会直接把他们占据的内存空间释放掉。
真的如书中所说,这种机制滴水不漏吗?会不会有陷阱?
会不会有一些不经意的操作导致引用计数永远不为零,然而用户却懵然不知呢?
内存泄漏真的可以在真正意义上得到避免吗?
Q3:垃圾回收机制究竟能干什么,不能干什么?究竟本质是什么?
垃圾回收机制原来只会对new出来的堆内存起作用!!!
万一不是new出来的,那还是得人工回收……
1、You might not get garbage collected!
哎……这样的垃圾回收机制啊……
还真是懒啊……
总之就是,垃圾回收机制只会回收对象在堆中的内存,但究竟这个对象的操作曾经干了什么,有没有“历史遗留问题”,java是一概不管的……
这个垃圾回收机制还是回到回忆中去吧……(我没吐槽最终幻想,真的没有!)
ClassName obj;//局部对象,放在栈中(C++可以这样,java不行)
C++的好处:作用域结束,局部对象的destructor自动被调用,释放栈中内存;
New出来的对象:
//C++的坏处:不执行delete的话,对象占用的内存会一直赖在堆中。就让内存漏一会儿吧。
//java的好处:不用显式执行,只要作用域结束,reference被清除,垃圾回收器就会自动回收堆中的内存;
而且,java兄还不让你在栈中创建局部对象呢……
Q3/1:那究竟new操作发生的时候,java语言为用户干了什么?new的操作也会对引用计数产生作用——例如初始化和创建吗?垃圾回收器如何工作呢?
相对于堆而言,在栈中释放和分配内存还是效率较高。这可能也是一些程序的数据放在栈中,一些放在堆中的原因之一吧?
引用计数类似是一个对象中的成员;有东西引用对象,就增加1,当有引用在栈中被释放或者设为NULL,就减少1;发现引用计数为0,就证明这个对象已经没人要了……
缺点:
垃圾回收器要扫描整个对象列表,查找引用计数为0的对象;
如果有两个对象碰巧相互引用了彼此,那这两个对象的引用计数就用不为零,即使没人要也不会被清除掉;
最悲催的是:
JVM都不是通过这种机制实现垃圾回收滴……
JVM是这么干的……
逆向思维,不找死的,找活的!从一个引用出发,遍历其对象-树(自己作的)。透过每一个在栈中或者在静态区中保存的引用,以之为根节点,遍历由他出发可以到达的对象节点。
好处:
不用遍历所有堆中的对象。
解决两个对象互相引用而导致引用计数恒不为0的问题;
经过上述处理,没被找到的对象会被清理掉,但是会留下内存碎片,浪费空间。所以……
妙!
把程序停止下来,把活动的对象copy到新的堆内存,连续存放,这样就腾出了那些原先成为碎片的空间。
然而,一直copy来copy去需要有额外的堆内存来保存copy的数据,实际上copy发生的时候需要双倍于被copy内容的堆内存同时可用。
其次,copy也需要时空开销……
于是……
JVM就把sweep-and-mark和stop-and-copy结合起来(thinking in java有详述)
大对象占用一个block,每个block有一个generation count作为其可用与否的标记。
一些小对象放在一个block里;
根据引用来遍历其对象-树的操作开始执行:
一般来说,大对象是不会被copy的;
小对象会被复制和重新管理,释放内存碎片;
JVM在碎片多的时候进行stop-and-copy来整理碎片,腾出空间;在堆内存足够和碎片不多的情况下,则只执行sweep-and-mark。
在这样的垃圾回收机制下,只要是new出来的东西,真的都能回收了。某程度上还真是滴水不漏啊……
显然是抄IBM大型机的外存管理嘛!数据集放在block中,被删除的数据集的block标记为不可用,新建的数据集放在后面的block中。当存储空间不够了,整理那些已经存在又可用的数据集,存放在一片连续空间中,把碎片重新整理为可用内存,真是……
抄吧抄吧,不是罪……
posted on 2011-03-04 20:49
ArthasLee 阅读(801)
评论(1) 编辑 收藏 引用 所属分类:
笔记和疑问