写在前面的话:本文是作者在阅读了文章底部的相关网站链接后,整理得出的学习资料,并非完全原创,切勿私自拷贝用作商业用途,欢迎大家留言进行学术讨论。如有侵犯到他人权利,请联系本人进行删除,谢谢合作!
1.什么时候回出发对象的回收?
答:a.对象没有引用
b.作用域发生未捕获异常
c.程序在作用域正常执行完毕
d.程序执行了System.exit()
e.程序发生意外终止(被杀进程等)
2.是不是一个对象被赋值为null以后就一定被标记为可回收对象了呢?
答:并不是所有对象被赋值为null之后就一定被标记为可回收,有可能会发生逃逸!
3.常见的垃圾收集算法
答:
(a)JDK1.2之前,使用的是引用计数器算法。即当这个类被加载到内存以后,就会产生方法区,堆栈、程序计数器等一系列信息。
(1)当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,同时引用计数器+1。
(2)当有新的引用的时候,引用计数器继续+1。
(3)而当其中一个引用销毁的时候,引用计数器-1。
(4)当引用计数器被减为零的时候,标志着这个对象已经没有引用了,可以回收了!
上述算法带来的问题:
example:
ObjA.obj = ObjB & ObjB.obj = Obj.A
即 对象A指向对象B 同时 对象B又指向对象A 当其他额外的引用消失后 对象A和对象B之间还存在一个相互的引用关系,然而二者皆已为垃圾,可以被回收了。
(b)跟搜索算法
程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。
-----------
| GC root |
-----------
|
-----------
| ObjA |
-----------
/ \
/ \
----------- ----------- -----------
| ObjB | | ObjC | | ObjD |
----------- ----------- -----------
| |
----------- ----------- -----------
| ObjF | | .... | | ObjE |
----------- ----------- -----------
根据上图,获取从GC root 出发的引用关系:
ObjB
/
GC root -> ObjA
\
ObjC — ....
未被引用到的节点有 ObjD、ObjF、ObjE
java中可以作为GC Root的对象(本地变量表)
1.虚拟机栈中引用的对象
2.方法区中静态属性引用对象
3.方法区中常量引用对象
4.本地方法栈中引用的对象(Native对象)
4.java中的引用及其分类
(a)强引用
***只要强引用在,垃圾回收器永远不会回收
example:Object obj = new Object();
(b)软引用
**非必须引用,内存溢出之前进行回收
example:
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null
sf对obj是一个软引用。通过sf.get()取到这个对象。
软引用类似用户实现缓存的功能,内存足够的情况下直接通过软引用取值提升速度,若内存不足,则删除这部分缓存数据,从真正的来源查询这些数据。
(c)弱引用
**第二次垃圾回收时回收
example:
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
短时间内通过弱引用取对应的数据,可以取得,但当执行第二次垃圾回收时,将返回null值
(d)虚引用(幽灵/幻影引用)
*垃圾回收时回收,无法通过引用取得对象值
example:
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回从内存中已经删除
每次垃圾回收的时候都会被回收,通过虚引用取到的对象值永远为null。主要用于检测对象已经从内存中删除。
5.对象在内存中的区域划分
对象在内存中回被分成5个区域,而每个区域的回收比例是不同的
(1)方法区 5%
(2)Java堆 70%-95%
(3)Java栈 100%
(4)程序计数器 0%
(5)本地方法栈 100%
(a)方法区主要存放类与类之间的数据,这部分被加载到内存后基本不会改变
(b)Java堆中的数据,用完之后马上进行回收
(c)Java栈中的数据遵循后进先出的原则,在取下数据之前,要先把栈顶的元素进行出栈,因而回收率为100%
(d)程序计数器,记录线程执行的信号等信息。该区域是唯一不会内存溢出的区域
(e)本地方法栈同(C)
注释:sunhostspot中方法区的数据因为回收率非常小,而且成本又高,性价比非常差。所以在sunhostspot虚拟机中是不回收的。但是在高性能分布式J2EE的系统中方法区也是会被回收的(自定义加载器导致类经常被加载和卸载),但是需满足一下苛刻条件:(1)所有实例被回收(2)加载该类的ClassLoader被回收(3)Class对象无法通过任何途径访问(包括反射)
6.垃圾收集后的回收算法
在3.中提到了垃圾的收集算法,而在收集之后要面临的就是垃圾回收机制,其算法有如下几类
(1)标记-清除算法
(2)复制算法
(3)标记-整理算法
(a)标记-清除算法
图一:引用关系图(根目录->A->C)
---------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲表
--------- ------------------- |
| 引用 | | |
----------- ---------
*******************分割线*************************
图二:第一步,从根目录开始扫描对存活的对象进行标记(标记的内存带$)
-------------
| 引用 |
--------- ---------------------
|$根目录| | $A | B | $C | | 空闲表
--------- --------------------- |
| 引用 | | |
----------- -----------
*******************分割线*************************
图三:第二步,标记完成后,再扫描整个空间中未被标记的空间,进行回收(回收了B)
-------------- ----------------
| 引用 | | |
--------- ------------------ |
| 根目录| | A | | C | | 空闲表
--------- -------------------- |
| 引用 | | |
----------- --------
*******************分割线*************************
标记-清除的算法不需要进行对象的移动,并且处理对象仅为未标记对象。若存活对象较多的情况下极为高效。但是由于对象不可移动,且直接回收不存活对象,会产生内存碎片。(如图三种B空间被回收后A、C之间产生内存碎片)。
(b)复制算法
图一:引用关系图(根目录->A->C)
---------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| 引用 | | |
----------- -----------
*******************分割线*************************
图二:从根目录扫描,将存活对象复制到一块新的,未被使用的空间
---------------
| 引用 |
--------- --------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| | 引用 | | | |
复 | ---------- | -----------
制 | |
| -------
| | 复制
-------------------
| A | c | | 空闲指针
------------------ |
| |
---------------------
*******************分割线*************************
复制算法采用从更目录开始扫描,并将存活的对象复制到一块新的,未被使用的空间。在存活对象较少的情况下极为高效。但是由于需要进行对象移动,所以需要一块内存交换空间
(c)标记-整理算法
图一:引用关系图(根目录->A->C)
-------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| 引用 | | |
------------- ---------------
*******************分割线*************************
图二:第一步,从根目录开始扫描对存活的对象进行标记(标记的内存带$)
-------------
| 引用 |
--------- ----------------------
|$根目录 | | $A | B | $C | | 空闲指针
--------- ---------------------- |
| 引用 | | |
------------- ------------------
*******************分割线*************************
图三:回收不存活对象,并且整理存活对象,消除内存空间
---------------
| 引用 |
--------- -------------------------
|根目录 | | A | C | | 空闲指针
--------- ------------------------- |
|引用 | | |
------- ---------------------
*******************分割线*************************
标记-整理算法采用和标记-清除算法一样的手法对对象进行标记,但在回收不存活的对象占用的空间后,将所有存活的对象往左端移动,并更新对应指针。成本最高,但是解决的内存碎片的问题。
相关参考网站:
http://blog.youkuaiyun.com/achuo/article/details/45250077
http://jbutton.iteye.com/blog/1569746
1.什么时候回出发对象的回收?
答:a.对象没有引用
b.作用域发生未捕获异常
c.程序在作用域正常执行完毕
d.程序执行了System.exit()
e.程序发生意外终止(被杀进程等)
2.是不是一个对象被赋值为null以后就一定被标记为可回收对象了呢?
答:并不是所有对象被赋值为null之后就一定被标记为可回收,有可能会发生逃逸!
3.常见的垃圾收集算法
答:
(a)JDK1.2之前,使用的是引用计数器算法。即当这个类被加载到内存以后,就会产生方法区,堆栈、程序计数器等一系列信息。
(1)当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,同时引用计数器+1。
(2)当有新的引用的时候,引用计数器继续+1。
(3)而当其中一个引用销毁的时候,引用计数器-1。
(4)当引用计数器被减为零的时候,标志着这个对象已经没有引用了,可以回收了!
上述算法带来的问题:
example:
ObjA.obj = ObjB & ObjB.obj = Obj.A
即 对象A指向对象B 同时 对象B又指向对象A 当其他额外的引用消失后 对象A和对象B之间还存在一个相互的引用关系,然而二者皆已为垃圾,可以被回收了。
(b)跟搜索算法
程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。
-----------
| GC root |
-----------
|
-----------
| ObjA |
-----------
/ \
/ \
----------- ----------- -----------
| ObjB | | ObjC | | ObjD |
----------- ----------- -----------
| |
----------- ----------- -----------
| ObjF | | .... | | ObjE |
----------- ----------- -----------
根据上图,获取从GC root 出发的引用关系:
ObjB
/
GC root -> ObjA
\
ObjC — ....
未被引用到的节点有 ObjD、ObjF、ObjE
java中可以作为GC Root的对象(本地变量表)
1.虚拟机栈中引用的对象
2.方法区中静态属性引用对象
3.方法区中常量引用对象
4.本地方法栈中引用的对象(Native对象)
4.java中的引用及其分类
(a)强引用
***只要强引用在,垃圾回收器永远不会回收
example:Object obj = new Object();
(b)软引用
**非必须引用,内存溢出之前进行回收
example:
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null
sf对obj是一个软引用。通过sf.get()取到这个对象。
软引用类似用户实现缓存的功能,内存足够的情况下直接通过软引用取值提升速度,若内存不足,则删除这部分缓存数据,从真正的来源查询这些数据。
(c)弱引用
**第二次垃圾回收时回收
example:
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
短时间内通过弱引用取对应的数据,可以取得,但当执行第二次垃圾回收时,将返回null值
(d)虚引用(幽灵/幻影引用)
*垃圾回收时回收,无法通过引用取得对象值
example:
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回从内存中已经删除
每次垃圾回收的时候都会被回收,通过虚引用取到的对象值永远为null。主要用于检测对象已经从内存中删除。
5.对象在内存中的区域划分
对象在内存中回被分成5个区域,而每个区域的回收比例是不同的
(1)方法区 5%
(2)Java堆 70%-95%
(3)Java栈 100%
(4)程序计数器 0%
(5)本地方法栈 100%
(a)方法区主要存放类与类之间的数据,这部分被加载到内存后基本不会改变
(b)Java堆中的数据,用完之后马上进行回收
(c)Java栈中的数据遵循后进先出的原则,在取下数据之前,要先把栈顶的元素进行出栈,因而回收率为100%
(d)程序计数器,记录线程执行的信号等信息。该区域是唯一不会内存溢出的区域
(e)本地方法栈同(C)
注释:sunhostspot中方法区的数据因为回收率非常小,而且成本又高,性价比非常差。所以在sunhostspot虚拟机中是不回收的。但是在高性能分布式J2EE的系统中方法区也是会被回收的(自定义加载器导致类经常被加载和卸载),但是需满足一下苛刻条件:(1)所有实例被回收(2)加载该类的ClassLoader被回收(3)Class对象无法通过任何途径访问(包括反射)
6.垃圾收集后的回收算法
在3.中提到了垃圾的收集算法,而在收集之后要面临的就是垃圾回收机制,其算法有如下几类
(1)标记-清除算法
(2)复制算法
(3)标记-整理算法
(a)标记-清除算法
图一:引用关系图(根目录->A->C)
---------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲表
--------- ------------------- |
| 引用 | | |
----------- ---------
*******************分割线*************************
图二:第一步,从根目录开始扫描对存活的对象进行标记(标记的内存带$)
-------------
| 引用 |
--------- ---------------------
|$根目录| | $A | B | $C | | 空闲表
--------- --------------------- |
| 引用 | | |
----------- -----------
*******************分割线*************************
图三:第二步,标记完成后,再扫描整个空间中未被标记的空间,进行回收(回收了B)
-------------- ----------------
| 引用 | | |
--------- ------------------ |
| 根目录| | A | | C | | 空闲表
--------- -------------------- |
| 引用 | | |
----------- --------
*******************分割线*************************
标记-清除的算法不需要进行对象的移动,并且处理对象仅为未标记对象。若存活对象较多的情况下极为高效。但是由于对象不可移动,且直接回收不存活对象,会产生内存碎片。(如图三种B空间被回收后A、C之间产生内存碎片)。
(b)复制算法
图一:引用关系图(根目录->A->C)
---------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| 引用 | | |
----------- -----------
*******************分割线*************************
图二:从根目录扫描,将存活对象复制到一块新的,未被使用的空间
---------------
| 引用 |
--------- --------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| | 引用 | | | |
复 | ---------- | -----------
制 | |
| -------
| | 复制
-------------------
| A | c | | 空闲指针
------------------ |
| |
---------------------
*******************分割线*************************
复制算法采用从更目录开始扫描,并将存活的对象复制到一块新的,未被使用的空间。在存活对象较少的情况下极为高效。但是由于需要进行对象移动,所以需要一块内存交换空间
(c)标记-整理算法
图一:引用关系图(根目录->A->C)
-------------
| 引用 |
--------- -------------------
|根目录 | | A | B | C | | 空闲指针
--------- ------------------- |
| 引用 | | |
------------- ---------------
*******************分割线*************************
图二:第一步,从根目录开始扫描对存活的对象进行标记(标记的内存带$)
-------------
| 引用 |
--------- ----------------------
|$根目录 | | $A | B | $C | | 空闲指针
--------- ---------------------- |
| 引用 | | |
------------- ------------------
*******************分割线*************************
图三:回收不存活对象,并且整理存活对象,消除内存空间
---------------
| 引用 |
--------- -------------------------
|根目录 | | A | C | | 空闲指针
--------- ------------------------- |
|引用 | | |
------- ---------------------
*******************分割线*************************
标记-整理算法采用和标记-清除算法一样的手法对对象进行标记,但在回收不存活的对象占用的空间后,将所有存活的对象往左端移动,并更新对应指针。成本最高,但是解决的内存碎片的问题。
相关参考网站:
http://blog.youkuaiyun.com/achuo/article/details/45250077
http://jbutton.iteye.com/blog/1569746