`

一段垃圾程序引出的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
12
2
分享到:
评论
10 楼 w7849516230 2012-07-29  
发现自己在CSDN的博客在这里被转载了,可惜没有标明。今天自己偶然看到,觉得这里的评论反而比CSDN要多。
9 楼 dyllove98 2012-06-10  
  不错
8 楼 aqhjh 2012-06-09  
java的垃圾回收器在内存使用告急的时候启动,并且回收的对象是通过查看对象是否还存在引用,如果不存在引用,就转为回收队列,如果存在引用,那么就不回收,并且进入回收队列的对象,也不一定被回收,需要经过一段时间之后,才进行性回收。进入回收队列的对象还是可以被重新使用的。
7 楼 447214075 2012-06-09  
垃圾回收好像还有一种网络拓扑形式的吧,回收那些不可到达的内存块。
6 楼 fengsky491 2012-06-09  
支持这个说法的,觉得还需要好好整理下吧,我现在就是用main循环启动线程
dwbin 写道
有点儿乱,实际上主线程退出了主线程启动的线程是不会退出的,这也在一定程度上引出了为什么使用线程池,原因就是线程不可控,主线程完成了,子线程的状态就处于了一种非托管的状态。

至于堆内存的溢出是因为你在循环里面不停的申请内存,但是因为你的代码中引用的作用空间是在循环体里面,所以每次循环完成的时候运行时栈应该会弹出,引用会丢弃的,垃圾回收的时候是可以回收掉的,所以可能是你的jvm内存设置的问题。

5 楼 kjj 2012-06-08  
没看懂,如果你要线程主线和子线和谐点,试试cutedown这类对象!!!
4 楼 tenderuser 2012-06-08  

dwbin 写道
有点儿乱,实际上主线程退出了主线程启动的线程是不会退出的,这也在一定程度上引出了为什么使用线程池,原因就是线程不可控,主线程完成了,子线程的状态就处于了一种非托管的状态。

至于堆内存的溢出是因为你在循环里面不停的申请内存,但是因为你的代码中引用的作用空间是在循环体里面,所以每次循环完成的时候运行时栈应该会弹出,引用会丢弃的,垃圾回收的时候是可以回收掉的,所以可能是你的jvm内存设置的问题。

那不是一点的乱,完全是一些无关的东西拼凑起来弄成的,估计lz自己也只是东弄点,西弄点,没自己的一点东西
3 楼 dwbin 2012-06-08  
有点儿乱,实际上主线程退出了主线程启动的线程是不会退出的,这也在一定程度上引出了为什么使用线程池,原因就是线程不可控,主线程完成了,子线程的状态就处于了一种非托管的状态。

至于堆内存的溢出是因为你在循环里面不停的申请内存,但是因为你的代码中引用的作用空间是在循环体里面,所以每次循环完成的时候运行时栈应该会弹出,引用会丢弃的,垃圾回收的时候是可以回收掉的,所以可能是你的jvm内存设置的问题。
2 楼 chairmanMao 2012-06-07  
1 楼 aijuans 2012-06-07  
正在研究IBM JVM的 rss增长问题,有空可以一起交流

相关推荐

    java程序设计教程

    本书从计算机软件设计员的角度出发,结合软件设计语言的发展趋势,就 Java 语言的应用与面向对象 ...从而引出 Java 面向对象程序设计思想。本书从 Java 语言和面向对象程序设计方法相结合入手,以大量实例

    Java 垃圾回收新算法刍探

    由Java语言与C/C++对象在内存管理方式的不同,引出了Java语言的优势技术——垃圾处理技术。通过对GC工作原理的阐述及对一些传统的垃圾收集器的分析,提出了一种新的垃圾处理算法,一定程度上改善和提高了Java垃圾...

    Java基础教案\JAVA精讲入门

     一个Java程序的开发过程  一个简单的Java应用程序的开发过程  什么是JSP 授课目的:  掌握Java语言的特点  掌握环境变量的配置  掌握Java程序的开发过程  了解JSP技术 授课重点及难点:  建立Java...

    《Java程序设计》教学活动设计.doc

    《Java程序设计》教学活动设计 第一讲 基本活动安排: 1. 介绍课程内容,上课方式,上课时间和地点。明确考核方式和学习要求。 2. 要求学生注册登录虚拟学习社区: htp://...

    JAVA反射机制详解视频

    (类的加载概述和加载时机) (类加载器的概述和分类) (获取class文件对象的三种方式) ...(通过反射写一个通用的设置某个对象的某个属性为指定的值) (通过用户的增删改查和学生的登录注册引出中介) (动态代理的概述和实现)

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    第1章 让自己的第一个Java程序跑起来 2 教学视频:19分钟 1.1 想要用Java改变这个世界吗? 2 1.1.1 Java有什么优势? 2 1.1.2 Java在哪儿? 3 1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 ...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    第1章 让自己的第一个Java程序跑起来 2 教学视频:19分钟 1.1 想要用Java改变这个世界吗? 2 1.1.1 Java有什么优势? 2 1.1.2 Java在哪儿? 3 1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 ...

    EAS模板引入引出

    EAS基础资料的引入引出流程和代码的实现应该对大家有用,共享一下。

    高中历史之历史百科宋刻本越绝书引出的一段公案素材

    高中历史之历史百科宋刻本越绝书引出的一段公案素材

    Java双缓冲技术.doc

    本文从J2SE的一个再现了屏幕闪烁的Java Appilication简单动画实例展开,对屏幕闪烁的原因进行了分析,找出了闪烁成因的关键:update(Graphics g)函数对于前端屏幕的清屏。由此引出消除闪烁的方法——双缓冲。双缓冲...

    Java 高级特性.doc

    5.在JAVA的程序中,我经常性的看到字符前面有@这种标志的符号.这个就叫做注解! 下面是使用 @SuppressWarnings 来取消 deprecation 警告的一个例子: public class Test { @Deprecated //在eclipse下运行的时候,...

    SpringBoot揭秘-快速构建微服务体系

    本书以微服务的基本概念介绍性开篇,逐步引出Java平台下打造微服务的利器SpringBoot微框架。书中从SpringBoot微框架的“出身”开始,循序渐进,为大家剖析SpringBoot微框架的设计理念和原理,并对框架的重点功能和...

    《世界地图引出的发现》思考题一.ppt

    《世界地图引出的发现》思考题一.ppt

    Java IO 体系.md

    Java IO 体系 - Java IO 体系 ...Java IO 是一个庞大的知识体系,很多人学着学着就会学懵了,包括我在内也是如此,所以本文将会从 Java 的 BIO 开始,一步一步深入学习,引出 JDK1.4 之后出现的 NIO

    java连接mysql

    文章主要描述的是java连接MYSQL数据库的正确操作步骤,在此篇文章里我们主要是以实例列举的方式来引出其具体介绍。先创建数据库: CREATE DATABASE SCUTCS; 接着,创建表: CREATE TABLE STUDENT ( SNO CHAR(7)...

    K3标准凭证引入、引出

    金蝶K3凭证引入引出的指导,适合新手使用!欢迎大家下载

    由一桩盗窃案引出的话题.docx

    由一桩盗窃案引出的话题.docx

    引出端及整体安装件强

    GBT 2423.60-2008 电工电子产品环境试验 第2部分:试验方法 试验U:引出端及整体安装件强度.pdf

    java继承及多态概念PPT

    本PPT对java继承的基本概念,语法及应用进行了讲解。在继承的基础上进一步讲解了由继承引出的方法重写及上塑造性,最后引出多态的概念以及相关代码实例

    Beatles9527#StudyNotes#_21Java8语法新特性1

    参考《Java注解》第三章内容四. 接口默认方法——Java也会出现多继承问题Java8新特性default关键字,引出Java多继承问题五. Java8新特性

Global site tag (gtag.js) - Google Analytics