`

一段垃圾程序引出的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.ppt

    接着,课程回顾了计算机语言的发展历程,从机器语言、汇编语言到面向过程和面向对象的语言,最后引出Java,一种具有组件化和跨平台能力的语言。 Java的历史部分讲述了Java的起源,起始于1991年的Set-top box项目,...

    Java 垃圾回收新算法刍探

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

    Java基础教案\JAVA精讲入门

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

    大班社会《垃圾桶的愿望》教案.pdf

    * 不可回收垃圾:生活中常见的不可回收垃圾,如餐巾纸、一次性筷子等。 * 有害垃圾:有毒、有害的垃圾,如电池、药品包装等。 知识点三:环保意识 * 环保的重要性:保护环境、维护生态系统、保障人类健康。 * 环保...

    exe4j (java程序转换成.exe)

    这就引出了一个问题:如何将用Java编写的程序转化为Windows用户友好的.exe格式?这就是exe4j这个工具的用途所在。 **exe4j简介** exe4j是由ej-technologies公司开发的一款强大的Java程序转换工具,它的主要功能是...

    java外文翻译.pdf

    Java的可移植性、面向对象的设计、健壮的库集合和垃圾回收机制使得它成为开发服务器端应用程序的理想选择。Java EE(Java Platform, Enterprise Edition)为构建企业级应用程序提供了一整套服务和API。这包括用于...

    大一班垃圾分类活动方案.pdf

    接着,活动进入认识垃圾分类标记环节,教师展示不同的垃圾分类标记,如可回收物、有害垃圾、厨余垃圾和其他垃圾等,并通过幼儿参与的操作活动,使他们了解不同类型的垃圾应如何分类。孩子们在实践中学习,将垃圾按照...

    java入门到精通PPT

    12. **JVM**:理解Java虚拟机的工作原理,包括内存管理(堆、栈、方法区等)、垃圾回收机制和性能优化技巧。 13. **设计模式**:学习常见的设计模式,如单例、工厂、装饰器、观察者等,提升代码的可维护性和可扩展性...

    大一班垃圾分类.docx

    1. 能够区分可回收垃圾、有害垃圾、厨余垃圾和其他垃圾,并能正确分类。 2. 养成不乱丢垃圾的习惯,初步建立环保意识。 3. 愿意参与维护环境卫生的行动,如主动分类投放垃圾。 活动准备包括经验准备和物质准备: 1....

    《垃圾分类》教案-八(5)班.doc

    * 通过讨论学生板演的结果,师生合作完成垃圾种类的划分:可回收垃圾、不可回收垃圾和有害垃圾。 * 让学生了解到垃圾回收利用的好处和重要性。 四、垃圾的处理方法 * 可回收垃圾的处理:主要处理方法是把可回收...

    JAVA反射机制详解视频

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

    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模板引入引出

    2. **数据迁移**:当需要将数据从一个环境迁移到另一个环境时,模板的引入引出功能提供了便捷的方式。 3. **数据交换**:不同部门之间或者与其他企业之间的数据交换可以通过模板的格式进行标准化处理。 #### 三、...

    smail分析转换工具 Smali2Java

    然而,Smali代码对于非专业人士来说,阅读和理解起来相当困难,这就引出了Smali2Java工具的出现。 Smali2Java的核心功能在于将Smali代码转换为Java源代码。这个过程极大地简化了逆向工程师的工作,因为Java代码对于...

    java反编译软件 class转java

    这就引出了我们的主题——"java反编译软件 class转java"。 反编译是将已编译的机器代码转换回源代码的过程。在Java世界里,这个过程就是将.class文件转换回.java文件。这样的工具可以帮助开发者查看和理解已有的...

    BOPPPS模型在Java程序设计课程中的应用.pdf

    在Java程序设计课程中,可以通过一个具体的程序功能或问题,引出学习的主题,让学生理解编程可以解决现实问题,从而激发学生的求知欲望。 “目标”阶段明确告诉学生本节课的具体学习目标。在Java课程中,这可能包括...

    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程序设计》第8章 类与对象教案.pdf

    【Java程序设计】类与对象是Java编程的核心概念,它们是面向对象编程的基础。类是对具有相似属性和行为的事物的抽象,而对象是类的实例,代表着具体存在的个体。 在【描述】中,该教案是为了2021年的高校教师资格证...

Global site tag (gtag.js) - Google Analytics