谈JAVA的内存回收

程序员需要通过关键字new创建Java对象,即可视为Java对象申请内存空间,JVM会在堆内存中为每个对象分配空间,当一个Java对象失去引用时,JVM的垃圾回收机制会自动清除他们,并回收它们所占用的内存空间。Java内存管理包括内存分配(创建Java对象时)和内存回收(回收Java对象)两个方面。这两方面工作都是由JVM自动完成的。

当Java对象被创建之后,垃圾回收机制会实时地监控每一个对象的运行状态,包括对象的申请,引用,被引用,赋值等。当垃圾回收机制实时地监控某个对象不再被引用变量所引用时,立即回收机制就会回收它所占用的空间。基本上,可以把JVM内存中对象引用理解成一种有向图,把引用变量,对象都当成为有向图的顶点,将引用关系当成图的有向边,有向边总是从引用端指向被引用的Java对象。因为Java所有对象都是由一条一条线程创建出来的,因此可把线程对象当成有向图的起始顶点。对于单线程而言,整个程序只有一条main线程,那么该图就是以main进程为顶点的有向图。在这个有向图中,main顶点可达的对象可达状态,垃圾回收机制不会回收它们;如果某个对象在这个有向图中处于不可达状态,那么就认为这个对象不再被引用,接下来垃圾回收机制就会主动回收它了。

class Node{

Node next;

String name;

public Node(String name){

this.name = name;

}

}

publicclass NodeTest {

publicstaticvoid main(String[] args){

Node n1 = new Node("The first node");

Node n2 = new Node("The second node");

Node n3 = new Node("The third node");

n1.next = n2;

n2 = null;

n3 = n2;

}

}

从main开始,有一条路径可带“The first node“,因此该对象处于可达状态,垃圾回收机制不会回收它;从main顶点开始,有两条路径可达”The second node”,因此该对象处于可达状态,垃圾回收机制也不会回收它;从main顶点开始,没有路径可以到达”The third node”,因此这个Java对象就变成了垃圾,接下来垃圾回收机制就会开始回收它。

采用有向图来管理内存中的对象具有高的精度,但是缺点是效率较低。

当一个对象在堆内存中运行时,根据它在对应有向图中的状态,可以把它所处的状态分成如下3种:

  1. 可达状态:当一个对象被创建后,有一个以上的引用变量引用它。在有向图中可从起始顶点到该对象,那它就处于可达状态,程序可通过引用变量来调用该对象的属性和方法。
  2. 可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将先进入可恢复状态,此时从有向图的起始顶点不能到达该对象。在这个状态下,系统的垃圾回收机制准备回收该对象所占用的内存。在回收该对象之前,系统会调用可恢复状态的对象的finalize方法进行资源清理,如果系统在调用finalize方法重新让一个以上引用变量引用该对象,则这个对象会再次变为可达状态;否则,该对象将进入不可达状态。
  3. 不可达状态:当对象的所有关联都被切断,且系统被调用所有对象的finalize方法依然没有使该对象变成可达状态,那这个对象将永久性地失去引用,最后变成不可达状态。只有当一个对象处于不可达状态时,系统才会真正回收该状态所占有的资源。

publicclass StatusTranfer {

publicstaticvoid test(){

String a = new String("aaa");

a = new String("bbb");

}

publicstaticvoid main(String[] args){

test();

}

}

当程序执行test方法的代码时,代码定义了一个a变量,并指向字符串”aaa”,字符串”aaa”处于可达状态,当执行完”bbb”后,“aaa“处于可恢复状态,”bbb”指向可达状态。

一个对象可以被一个方法局部变量所引用,也可以被其他类的类变量引用,或者被其他对象的实例变量性引用。当某个对象被其他类的类变量引用时,只有该类被销毁后,该对象才会进入可恢复状态;当某个对象被其他对象的实例变量引用时,只有当引用该变量的对象被销毁或变成不可达状态后,该对象才会进入不可达状态。

为了更好地管理对象的个,从JDK1.2开始,Java在java.lang.ref包下提供了3个类:SoftReference,PhantomReference和WeakReference.

程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。

Java程序可通过强引用来访问实际的对象,当一个对象被一个或多个强引用变量所引用时,它处于可达状态,它不可能被系统垃圾回收机制回收,即使是内存非常紧张的时候。因此它容易造成内存泄露。

软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它很可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,不会被系统回收。

当程序需要大量创建某个类的新对象,而且有可能重新访问已创建来对象时可以充分使用软引用来解决内存紧张的难题。

例如,需要访问1000个Person对象,可以有两种方式:

  1. 一次创建1000个Person对象,但只有一个Person引用指向最后一个Person对象;
  2. 定义一个长度为1000的Person数组,每个数组元素引用一个Person对象。

第一种情况,程序不允许需要重新访问前面创建的对象,即使这个对象所占的堆空间还没有被回收。但已经失去了这个对象的引用,因此也不得不重新创建一个新的Person对象(重新分配内存)。而那个已有的Person对象则是等待被回收。

第二种情况,优势是可以随时重新访问前面创建的每个Person对象。弱点是如果系统堆内存空间紧张,而1000个Person对象都被强引用引用着,垃圾回收机制也不可能回收它们的堆内存空间,系统性能差。

如果使用软引用则是比较好的方案。如下:

import java.lang.ref.SoftReference;

class Person{

String name;

intage;

public Person(String name,int age){

this.name = name;

this.age = age;

}

public String toString(){

return"Person[name="+name+",age="+age+"]";

}

}

publicclass SoftReferenceTest {

publicstaticvoid main(String[] args){

SoftReference<Person>[] people = new SoftReference[10000000];

for(int i=0;i<people.length;i++){

people[i] = new SoftReference<Person>(new Person("name"+i,(i+1)*4%100));

}

System.out.println(people[2].get());

System.out.println(people[4].get());

System.gc();

System.runFinalization();

System.out.println(people[2].get());

System.out.println(people[4].get());

}

}

Output:

谈JAVA的内存回收(一)的更多相关文章

  1. 从Java虚拟机的内存区域、垃圾收集器及内存分配原则谈Java的内存回收机制

    一.引言: 在Java中我们只需要轻轻地new一下,就可以为实例化一个类,并分配对应的内存空间,而后似乎我们也可以不用去管它,Java自带垃圾回收器,到了对象死亡的时候垃圾回收器就会将死亡对象的内存回 ...

  2. 浅谈Java的内存模型以及交互

    本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图:              程序计数器:用来记录当前线程执行到哪一步操作.在多 ...

  3. Java的内存回收机制

    原文出处: cnblogs-小学徒V 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C ...

  4. [转载]Java的内存回收机制

    转自:http://www.admin10000.com/document/1671.html 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由J ...

  5. [java,2017-05-15] 内存回收 (流程、时间、对象、相关算法)

    内存回收的流程 java的垃圾回收分为三个区域新生代.老年代. 永久代 一个对象实例化时 先去看伊甸园有没有足够的空间:如果有 不进行垃圾回收 ,对象直接在伊甸园存储:如果伊甸园内存已满,会进行一次m ...

  6. Java jvm 内存回收机制

    http://blog.csdn.net/yaerfeng/article/details/51291903 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方 ...

  7. Java的内存回收机制详解X

    http://blog.csdn.net/yqlakers/article/details/70138786 1 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前 ...

  8. Java的内存回收

    一.java引用的种类 1.对象在内存中的状态 可达状态:当一个对象被创建后,有一个以上的引用变量指向它. 可恢复状态: 不可达状态:当对象的所有关联被切断,且系统调用所有对象的finalize方法依 ...

  9. 浅谈Java堆内存分代回收

    目录 1.概述 2.堆内存是如何分代的 3.各分代之间是如何配合工作的 1.概述 与C++不同的是, 在Java中我们无需关心对象占用空间的释放, 这主要得益于Java中的垃圾处理器(简称GC)帮助我 ...

随机推荐

  1. WORDPRESS插件开发(二)HELLO WORLD改进版

    在上一篇文章中WORDPRESS插件开发(一)HELLO WORLD,演示了Hello World的最简单实现,只是在每篇文章的后面加入Hello World字符,而且字符也是写死的. 如果用户需要自 ...

  2. 初识pngdrive

    初识是第一次认识的意思,类似的词还有初见.初遇.初心.初愿.初恋.初吻……梦里相见如初识,很美好的感觉.同样,今天我们要认识的也是一个比较神奇美妙的东西,至少对于程序员来说. 我曾经尝试过很多文件加密 ...

  3. 更加直观地了解hasLayout和BFC

    网络上有很多关于hasLayout和BFC相关的文章,但是大部分都显得有些晦涩难懂.所以想用一些比较直观的例子来说明hasLayout和BFC给平时的布局带来的影响. 基础知识 在讲hasLayout ...

  4. 已经安装php后,再增加扩展模块(不重新编辑php)

    下面以安装curl为例,介绍具体安装步骤. 1.安装crul wget http://curl.haxx.se/download/curl-7.19.6.tar.gz tar -zxvf curl-7 ...

  5. [转]left join,right join,inner join区别

    left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录inner join(等值连接) 只 ...

  6. 如何在版本控制工具中管理Sencha Architect的項目

    根據數次痛苦的經歷結合stack overflow上的解答,發現原來還是可以使用svn.git之類的版本控制工具管理Sencha Architect生成的項目的. 具體的要點如下,假定項目記作{PRO ...

  7. Omnithreadlibary学习(3)-IOmniTask异步执行SendMessage

    在任务中发送消息, 可以是函数或者对象方法 TOmniTaskMessageEvent = procedure(const task: IOmniTaskControl; const msg: TOm ...

  8. Makefile与shell脚本的区别

    引用博客:Makefile与shell脚本区别 在Makefile可以调用shell脚本,但是Makefile和shell脚本是不同的.本文试着归纳一下Makefile和shell脚本的不同. 1.s ...

  9. nutch2.2.1

    http://blog.csdn.net/leave00608/article/details/17442163 https://svn.apache.org/repos/asf/nutch/tags ...

  10. python面向对象编程实例解析

    1. 类和函数 面向对象编程的例子: #!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): #在属性和变量的前面增加“ ...