转自:http://www.cnblogs.com/aigongsi/archive/2012/04/06/2434771.html
?
Java与C,C++有很大的不同就是java语言开发者不需要关注内存信息,不会显式的直接操作内存,而是通过JVM虚拟机来实现。
Java虚拟机运行的时候内存分配图如下图:
?
JVM虚拟机栈:一个是线程独有的,每次启动一个线程,就创建一个jvm虚拟机栈,线程退出的时候就销毁。这里面主要保存线程本地变量名和局部变量值。
本地方法栈: 调用本地jni方法的时候而创建的。这里分配的jvm之外的内存空间。方法调用结束之后销毁。
pc寄存器 : 这个保存线程当前执行的字节码指令
堆:主要保存创建的对象。
方法区:保存class相关的信息。主要是class的一个内存结构信息
常量池:方法区的一部分,主要保存class内存结构中常量值 例如String值,public static final 类型的值
?
我们这里说的垃圾回收,主要是Java虚拟机对堆内存区域的回收。
?
目前有两种算法
每个对象上都有一个引用计数,对象每被引用一次,引用计数器就+1,对象引用被释放,引用计数器-1,直到对象的引用计数为0,对象就标识可以回收
这个可以用数据算法中的图形表示,对象A-对象B-对象C 都有引用,所以不会被回收,对象B由于没有被引用,没有路径可以达到对象B,对象B的引用计数就就是0,对象B就会被回收。
?
?
但是这个算法有明显的缺陷,对于循环引用的情况下,循环引用的对象就不会被回收。例如下图:对象A,对象B 循环引用,没有其他的对象引用A和B,则A和B 都不会被回收。
?
这种算法目前定义了几个root,也就是这几个对象是jvm虚拟机不会被回收的对象,所以这些对象引用的对象都是在使用中的对象,这些对象未使用的对象就是即将要被回收的对象。简单就是说:如果对象能够达到root,就不会被回收,如果对象不能够达到root,就会被回收。
如下图:对象D访问不到根对象,所以就会被回收
以下对象会被认为是root对象:
- 被启动类(bootstrap加载器)加载的类和创建的对象
- JVM运行时方法区类静态变量(static)引用的对象
- JVM运行时方法去常量池引用的对象
- JVM当前运行线程中的虚拟机栈变量表引用的对象
- 本地方法栈中(JNI)引用的对象
由于这种算法即使存在互相引用的对象,但如果这两个对象无法访问到根对象,还是会被回收。如下图:对象C和对象D互相引用,但是由于无法访问根,所以会被回收。
JVM在确定是否回收的对象的时候采用的是root搜索算法来实现。
在root搜索算法的里面,我们说的引用这里都指定的是强引用关系。所谓强引用关系,就是通过用new 方式创建的对象,并且显示关联的对象
1monospace !important; font-size: 12px !important; background: none !important;" class="java plain">Object obj =
new
?Object();
以上就是代表的是强引用关系,变量obj 强引用了 Object的一个对象。
java里面有四种应用关系,从强到弱分别为:
Strong Reference(强引用) –>Weak Reference (弱引用) -> Soft Reference(软引用) – > Phantom Reference(引用)
?
Strong Reference : 只有在引用对象root不可达的情况下才会标识为可回收,垃圾回收才可能进行回收
Weak Reference :即使在root算法中其引用的对象root可达到,但是如果JVM堆内存不够的时候,还是会被回收。
Soft Reference : 无论其引用的对象是否root可达,在响应内存需要时,由垃圾回收判断是否需要回收。
Phantom Reference :在回收器确定其指示对象可另外回收之后,被加入垃圾回收队列.
?
下面可以看一个测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29public
?class
?ReferenceTest {
?
????
public
?static
?final
?Map<Integer, Reference> map =
new
?HashMap<Integer, Reference>();
?
????
public
?static
?void
?main(String[] args) {
????????
for
?(
int
?i =
0
; i <
1000
; i++) {
????????????
map.put(i,
new
?WeakReference(
new
?ReferenceObject(i)));
????????
}
?
????????
int
?i =
0
;
????????
for
?(Reference r : map.values()) {
????????????
if
?(r.get() ==
null
) {
????????????????
i++;
????????????
}
????????
}
????????
System.out.println(
"被回收的对象数:"
?+ i);
????
}
?
????
static
?class
?ReferenceObject {
????????
private
?int
????i;
?
????????
private
?byte
[] b;
?
????????
public
?ReferenceObject(
int
?i) {
????????????
this
.i = i;
????????????
b =
new
?byte
[
1024
?*
10
];
????????
}
????
}
}
这里创建大约1000个 10K的 Weak Reference 对象,最后打印的结果是:被回收的对象数:767,这里ReferenceObject如果设置为1K的话,最后的打印结果是0
这个例子并不严谨,但是却说明了被Weak Reference的对象在一定的时候会被jvm回收,但是强引用就不会出现这种状态。