一段垃圾程序引出的Java垃圾回收机制

出来混的,总是要还的。看来做软件,写代码也是这样啦!这篇应该算是Java编程思想阅读笔记的续集,由一段写得非常垃圾的程序引起,牵出了垃圾回收等一些相关知识,至于原来程序出现的堆溢出(java.lang.OutOfMemoryError: Java heap space)原因,还得继续寻找。下面先看一段类似的垃圾代码:

Java代码 
  1. package com.javatest.gc.lixuan;  
  2.   
  3. import java.lang.ref.SoftReference;  
  4.   
  5. public class GCTest {  
  6.     public static void main(String[] args) {  
  7.         final int MAX_THREAD_NUM = 20;  
  8.         int threadTotalNum = 0;  
  9.         for (; threadTotalNum < MAX_THREAD_NUM; threadTotalNum++) {  
  10.             NewThread nt = new NewThread();  
  11.             NewThread.setThreadNum();  
  12.             nt.start();  
  13.             // nt = null;  
  14.             try {  
  15.                 Thread.sleep(1000);  
  16.             } catch (InterruptedException e) {  
  17.                 // TODO Auto-generated catch block  
  18.                 e.printStackTrace();  
  19.             }  
  20.         }  
  21.         // System.out.println("I'm out!");  
  22.     }  
  23.   
  24. }  
  25.   
  26. class NewThread extends Thread {  
  27.     public static Integer threadNum = 0;  
  28.     public int mNum = 0;  
  29.     private int mStringBuilderNum = 0;  
  30.     private String str = "nihao!";  
  31.   
  32.     public NewThread() {  
  33.         synchronized (NewThread.threadNum) {  
  34.             mNum = threadNum;  
  35.         }  
  36.     }  
  37.   
  38.     public void run() {  
  39.         while (true) {  
  40.             try {  
  41.                 int n = 10000;  
  42.                 StringBuilder[] sb = new StringBuilder[n];  
  43.                 SoftReference<StringBuilder[]> sf = new SoftReference<StringBuilder[]>(sb);  
  44.                 for (int i = 0; i < n; i++) {  
  45.                     sb[i] = new StringBuilder();  
  46.                     sb[i].append(str);  
  47.                     Thread.sleep(10);  
  48.                     System.out.println("Thread number:" + mNum);  
  49.                 }  
  50.                 System.out.println("Thread number:" + mNum +"("+this.getId()+")"" new sb numbers:" + mStringBuilderNum++);  
  51.                 // Thread.sleep(1000);  
  52.             } catch (Exception e) {  
  53.                 e.printStackTrace();  
  54.             }  
  55.         }  
  56.     }  
  57.   
  58.     public static void setThreadNum() {  
  59.         synchronized (threadNum) {  
  60.             threadNum++;  
  61.         }  
  62.     }  
  63. }  


首先说明代码,一个线程类型(NewThread),就是在死循环中不停的创建新对象,当然里面标识了线程号;另一个类就是main所在了,循环创建一定数量的NewThread对象,然后启动。初衷是想建立一定数量的线程,让线程循环执行,结果其实忽略了几个问题:
1.线程对象是在循环体中创建的,那其作用域的问题;
2.线程创建完成后,main函数也就是主线程运行完毕了


针对第一个问题就是今天记录的主题,Java中的垃圾回收机制。先从《Java编程思想》中汲取,有C/C++的基础,有时候并不见得是好事,在C/C++中总会想着函数返回时内存的回收,动态申请的内存要主动释放,而且new申请的对象会在释放时自动调用析构函数;而在Java中不用过多的考虑,但是也放心不下,垃圾收集器什么时候,回收怎样的对象占用的内存呢?Java是在堆上分配对象,采用一些回收方法,目标是把不会再用到的对象内存释放掉,当然它没有那么聪明,知道你以后不会再用哪些,一般便于理解说明的是“引用计数”方式,即每个对象都含有一个引用计数器,当有引用指向对象时,引用数加1,当引用离开作用域或被置null时,引用计数减1,当运行的后台回收线程发现某个对象引用为0时,就释放其内存,这里有个问题是如果有循环引用(还有我上面那个程序你会发现问题),那么就无法正确释放内存;因此又有了“停止——复制”方式,这种方式是先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆中,没有被复制的全部都是垃圾,而且这种方式能够使新堆保持紧凑,但是这种方式消了较低,一方面暂停了程序运行,至少比实际需要多一倍的空间,另一方面,在程序进入稳定状态后,可能只有少量或没有垃圾,如此垃圾回收就显得浪费;其它还有些方式,可以查看参考【1】。
那么如此,就有个疑问了,我的main所在的类中声明的线程类型对象,是在for循环当中,相当于每次都离开了作用域,会不会for循环完之后就都退出了呢?而且在C/C++中,main的退出意味着程序的退出,那么这个程序是不是才刚创建完所有线程就退出了呢?我们再看Java的守护线程和非守护线程,
守护线程通常是由虚拟机自己使用的,比如执行垃圾收集任务的线程;
Java程序可以把它任何创建的线程标记为守护线程;
Java初始线程(即开始于main方法的线程)是非守护线程;
只要还有任何非守护线程在运行,那么这个Java程序也在运行,即这个JVM实例还存活着;当JVM中的所有非守护线程都终止时,JVM实例将自动退出;(参考【2】)

好了,顺带的第2个问题也知道了,那么堆溢出是怎么回事呢?貌似这里不会产生啊,可以看到其使用情况(VisualVM好用的软件啊)截图如下:

垃圾回收器及时收回内存。有无休眠直接影响CPU的占用。后图多了个软引用,可以看到回收的延迟。

从代码中还看到使用了SoftReference,这个其实就是三种引用:弱引用、软引用、虚引用;大概意思就是弱引用可以再对象置null时和垃圾回收之前延长点时间再回收,和finalize类似,只是finalize中包含了回收前执行的方法(主要是在调用了C/C++中的代码要执行的内存释放);软引用多用于缓冲区,在内存告急时才会被释放;虚引用则是主要用来跟踪对象被垃圾回收的活动,和引用队列(ReferenceQueue)联合使用,在回收前做点操作(上面只是试了软引用,从截图也能看出些东西来,详细的可以看参考3)。但是这样堆溢出还是没有找到原因,还是继续寻找吧。
参考:
【1】详细介绍Java垃圾回收机制 http://www.cnblogs.com/laoyangHJ/articles/java_gc.html
【2】Java虚拟机学习笔记(三)Java虚拟机 http://diecui1202.iteye.com/blog/606046
【3】Java基础 之软引用、弱引用、虚引用 http://www.cnblogs.com/blogoflee/archive/2012/03/22/2411124.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值