java gc总结

java与C,c++有很大的不同就是java语言开发者不需要关注内存信息,不会显式的直接操作内存,而是通过jvm虚拟机来实现。

java虚拟机运行的时候内存分配图如下图:

1

 

jvm虚拟机栈:一个是线程独有的,每次启动一个线程,就创建一个jvm虚拟机栈,线程退出的时候就销毁。这里面主要保存线程本地变量名和局部变量值。

本地方法栈: 调用本地jni方法的时候而创建的。这里分配的jvm之外的内存空间。方法调用结束之后销毁。

pc寄存器 : 这个保存线程当前执行的字节码指令

堆:主要保存创建的对象。

方法区:保存class相关的信息。主要是class的一个内存结构信息

常量池:方法区的一部分,主要保存class内存结构中常量值 例如String值,public static final 类型的值

 

我们这里说的垃圾回收,主要是java虚拟机对堆内存区域的回收。

 

1 首先的问题是:jvm如何知道那些对象需要回收 ?

目前有两种算法

  • 引用计数法

每个对象上都有一个引用计数,对象每被引用一次,引用计数器就+1,对象引用被释放,引用计数器-1,直到对象的引用计数为0,对象就标识可以回收

这个可以用数据算法中的图形表示,对象A-对象B-对象C 都有引用,所以不会被回收,对象B由于没有被引用,没有路径可以达到对象B,对象B的引用计数就就是0,对象B就会被回收。

 

 2

但是这个算法有明显的缺陷,对于循环引用的情况下,循环引用的对象就不会被回收。例如下图:对象A,对象B 循环引用,没有其他的对象引用A和B,则A和B 都不会被回收。

 3

  • root搜索算法

这种算法目前定义了几个root,也就是这几个对象是jvm虚拟机不会被回收的对象,所以这些对象引用的对象都是在使用中的对象,这些对象未使用的对象就是即将要被回收的对象。简单就是说:如果对象能够达到root,就不会被回收,如果对象不能够达到root,就会被回收。

如下图:对象D访问不到根对象,所以就会被回收

4

以下对象会被认为是root对象:

  • 被启动类(bootstrap加载器)加载的类和创建的对象
  • jvm运行时方法区类静态变量(static)引用的对象
  • jvm运行时方法去常量池引用的对象
  • jvm当前运行线程中的虚拟机栈变量表引用的对象
  • 本地方法栈中(jni)引用的对象

由于这种算法即使存在互相引用的对象,但如果这两个对象无法访问到根对象,还是会被回收。如下图:对象C和对象D互相引用,但是由于无法访问根,所以会被回收。

5

jvm在确定是否回收的对象的时候采用的是root搜索算法来实现。

在root搜索算法的里面,我们说的引用这里都指定的是强引用关系。所谓强引用关系,就是通过用new 方式创建的对象,并且显示关联的对象

1
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
29
public  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回收,但是强引用就不会出现这种状态。

posted @  2012-04-06 15:25  God Is Coder 阅读( 12951) 评论( 5编辑  收藏
  
#1楼   2012-04-18 14:47  houjinxin   
请问楼主这里的Map<Integer, Reference> map 算是root对象吗
  
#2楼   2012-04-18 14:56  houjinxin   
再问楼主 如何证明强引用不会出现这种现象
  
#3楼 [ 楼主2012-04-18 15:01  God Is Coder   
@ houjinxin
因为
第一 :ReferenceTest对象 当前执行线程引用的一个对象 ,当前执行线程里面引用的对象都是root,所以ReferenceTest对象不会被回收

第二:只要代码里面任何地方 ReferenceTest被引用到,都需要加载,验证,初始化三个步骤,当初始化一个Class的时候,jvm会调用静态初始化方法,map对象就被初始化了。由于是static类型的,会在ReferenceTest常量池里面对建立对Map的引用,然后在堆内存里面分配空间给Map的实例。由于方法区里面始有对map的引用(方法区在持久代),除非jvm回收持久代ReferenceTest,否则map在jvm生命周期内不会被回收。如果需要保存大量的信息到map中,oom,可以把map中持有的对象弱引用化
  
#4楼   2012-04-18 15:04  houjinxin   
import java.util.*;
import java.lang.ref.*;

public class ReferenceTest { 

//public static final Map<Integer, Reference> map = new HashMap<Integer, Reference>(); 
public static final Map<Integer, Object> map = new HashMap<Integer, Object>(); 
@SuppressWarnings("unchecked")
public static void main(String[] args) { 
for (int i = 0; i < 1000; i++) { 
//map.put(i, new WeakReference(new ReferenceObject(i))); 
map.put(i, new ReferenceObject(i)); 

System.out.println("map的大小:" + map.size()); 
int i = 0;
/**
for (Reference r : map.values()) { 
if (r.get() == null) { 
i++; 


**/

for (Object r : map.values()) { 
if (r == null) { 
i++; 



System.out.println("之后map的大小:" + map.size()); 
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]; 


}
我是这样验证的不知道对不对
map的大小:1000
之后map的大小:1000
被回收的对象数:0
输出的结果确实是0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值