Java一个重要的优势就是通过垃圾管理器GC (Garbage Collection)自动管理和回收内存,程序员无需通过调用方法来释放内存。也因此很好多的程序员可能会认为Java程序不会出现内存泄漏的问题,这种想法是不对的,当我们对内存使用不当的时候仍然可能会出现内存泄漏,并且问题相对与c++来说更隐秘,问题的根源排查起来也比较困难。不过,当我们了解了Java虚拟机内存区域,Java垃圾收集器之后,对于解决内存泄漏的问题也就不是什么困难的事情了。 

  相关阅读

  1、深入理解java虚拟机之java内存区域

  在前一篇博客中已经已经详细分析了java内存运行时各个区域,其中程序计数器、虚拟机栈、本地方法栈随着线程而生,随着线程而灭亡,操作数栈中的栈帧随着方法的执行的有条不紊地进行着入栈和出栈的操作,每个栈帧中分配多少内存基本上在类结构确定下来时就已经确定了大小了,因此这几个区域的内存分配和回收都具备确定性,在这个几个区域中就不需要过多的考虑内存分配的问题了,因为随着线程的结束,内存在然而然地就已经被回收了。而java堆和方法区不一样,一个接口中多个实现类所需要的内存可能不一样,一个方法中多个分支需要的内存的大小也有可能是不同的,我们在程序的运行期才能确定哪些对象需要创建,创建所需内存是多大,这些内存都是动态分配的,垃圾回收器考虑的就是这部分的内存,接下来我们将围绕以下3个问题来展开描述java垃圾收集器是如何自动回收内存的:

  1、哪些内存需要回收?

  2、什么时候回收?

  3、如何回收?

  本片博客就围绕这第一个问题展开说明,其余连个问题将在后面的博客中一一详解;

  首先,Java垃圾回收器的一个想要解决的问题是什么样的内存需要被回收呢?Java垃圾收集器认为,当一个对象再无被其它对象引用时可以认为这个对象可以回收了。那么垃圾收集器是怎么知道对象的死亡的还是存活的呢?目前,Java虚拟机有两种算法来确定哪些对象是无用的,需要被回收的。

  1.引用计数法

  该算法的思路是给每个对象都添加一个引用计算器,每当有其它对象引用时计数器就+1,当引用失效时计数器-1,任何时刻当该对象的引用数为0的时候,则判定这个对象不会再被使用了,可以将该对象回收了。这种算法实现起来很简单,效率也非常高,但是并没有被Java所采用,原因是这种算法很难解决对象相互引用的问题。看一下下面例子的代码:

public class Test {

    private Object instance = null;
private static final int _1MB = 1024 * 1024; /** 这个成员属性唯一的作用就是占用一点内存 */
private byte[] bigSize = new byte[2 * _1MB]; public static void main(String[] args)
{
Test A= new Test();
Test B = new Test();
A.instance = B;
B.instance = A;
A = null;
B = null; System.gc();
}
}

  运行结果:

[GC (System.gc())  9299K->720K(249344K), 0.0010947 secs]
[Full GC (System.gc()) 720K->627K(249344K), 0.0075042 secs]

  上面的例子中A和B相互引用,它们的引用计数并不为0,但是执行System.gc()方法后仍然被回收了,这说明了Java垃圾收集器并不是使用引用计数法的。

  2.可达性分析算法

  可达性分析算法就是Java垃圾收集器判断‘垃圾’对象的算法。基本思路是通过一系列的”GC Roots“ 的对象作为起点,从这些节点开始向下搜索,搜索所走过的路程叫做引用链,当一个对象没有任何引用链与”GC Roots“有链接时,那么可以判定这个对象是无用的对象。可以作为GC Roots对象的包括一下几种:

  1)Java虚拟机栈中局部变量表引用的对象;

  2)本地方法栈中JNI所引用的对象;

  3)方法区中的静态变量;

  4)方法区中常量引用的对象;

  下图为GC Roots:

  图中obj1 ~ obj7都能够直接或间接地与GC Roots有连接,因此他们是存活对象,不会被GC回收。obj8基本上可以被回收了,obj9和obj10虽然有相互引用,但是他们的引用链中并没有达到GC Roots,因此也会判定为非存活对象。

  3.引用的四种状态

  在jdk1.2之前,Java的引用类型是比较简单的,只有被引用和未引用两种状态,类型过于简单的话不利于垃圾收集器的回收,当已经将未被引用的对象都被回收之后内存仍然比较紧张时,垃圾收集器将无法确认被引用的对象是否需要回收,哪些对象可以被回收等。jdk1.2后Java对引用类型进行的拓展,包括:强引用、软引用、弱引用和虚引用四种情况,四者的引用强度依次递减。这样在虚拟机中内存使用不同的情况下,分别回收不容引用类型的对象。

  1)强引用

  在程序中我们直接new出来的对象的引用都属于强引用,比如:Object obj = new Object();只要强引用还在,就不会被GC回收。

  2)软引用

  描述部分部分有用但非必须的对象。在系统将要发生内存溢出的时候,虚拟机会尝试从这部分的引用类型中进行二次回收,如果二次回收后的内存仍不够用才会抛出内存溢出异常。Java中的类SoftReference表示软引用。

  3)弱引用

  描述被必须对象。被弱引用关联的对象只能生存到下一次回收之前,在垃圾收集器工作之后,无论内存是否够用,这类的对象都会被回收掉。Java中的类WeakReference表示弱引用。

  4)虚引用

  被虚引用关联的对象在被回收时会收到系统的通知。被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用。

  对于可达性分析算法,对于那些没有与GC Roots关联的对象并非是立即回收的,而是经历过两次标记后仍然没有与GC Roots关联上,此时才会回收该对象。对象在进行可达性分析后如果没有与GC Roots关联,则会进行第一次标记和第一次筛选,筛选条件为是否有必要执行finalize方法,比如如果finalize方法是否被覆盖,或是否已被执行一次。如果没有被覆盖或已被执行了,基本可以确认该对象会被回收了。否则,将这个对象放到F-Queue队列中。对F-Queue队列二次标记,如果在这次标记中对象成功关联上GC Roots,则该对象拯救了自己,将从”即将回收“的集合中移除,否则将会被回收。然而,对象只能拯救自己一次,第二次就会被回收了。

迎大家关注公众号: 【java解忧杂货铺】,里面会不定时发布一些技术干货博客;关注即可免费领取大量最新,最流行的技术教学视频:

深入理解java虚拟机之垃圾收集器的更多相关文章

  1. 深入理解Java虚拟机:垃圾收集器与内存分配策略

    目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...

  2. 《深入理解Java虚拟机》垃圾收集器

    说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态 ...

  3. 《深入理解Java虚拟机》——垃圾收集器与内存分配策略

    GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作: ...

  4. 深入理解Java虚拟机笔记——垃圾收集器与内存分配策略

    目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...

  5. 深入理解java虚拟机(3)垃圾收集器与内存分配策略

    一.根搜索算法: (1)定义:通过一系列名为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时 ...

  6. 深入理解JVM虚拟机-2垃圾收集器

    这里讨论的收集器基于JDK 1.7 Update 14之后的HotSpot虚拟机. 如果两个收集器之间存在连线,说明可以搭配使用.虚拟机所处的区域,则表示它是属于新生代收集器还是年老代收集器.在这里我 ...

  7. Java虚拟机学习 - 垃圾收集器

    HotSpot JVM收集器 上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器.如果两个收集器之间存在连线,就说明它们可以搭配使用. Serial(串行GC)收集器 Serial收集 ...

  8. Java虚拟机学习 - 垃圾收集器 (4)

    HotSpot JVM收集器 上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器.如果两个收集器之间存在连线,就说明它们可以搭配使用. Serial(串行GC)收集器 Serial收集 ...

  9. java虚拟机(六)--垃圾收集器和内存分配策略

    目前没有完美的收集器,不同的厂商.版本的虚拟机提供的垃圾收集器会有很大的差别,用户根据自己应用特点和要求组合出各个年代所使用 的收集器.基于jdk1.7Update14之后的虚拟机. HotSpot的 ...

随机推荐

  1. Python--简单接口测试实例(一)

    适用人员:初学python的测试人员,若对抓包不太清楚的可先学习抓包的知识 接口测试流程:发送请求-->返回响应-->结果判定-->生成报告 案例:下面以[今目标]新建客户为例来进行 ...

  2. Colossus: Successor to the Google File System (GFS)

    Colossus is the successor to the Google File System (GFS) as mentioned in the recent paper on Spanne ...

  3. 爬取廖雪峰的python3教程

    从廖雪峰老师的python教程入门的,最近在看python爬虫,入手了一下 代码比较low,没有用到多线程和ip代理池 然后呢,由于robots.txt的限定,构建了一些user-agent,并放慢的 ...

  4. 下载Github上某个项目的子文件夹和单个文件

    preface Github下的项目可能很大,里面有很多的子文件夹,我们可能只需要使用某个子目录下的资源,可以不用下载完整的repo就能使用. 例如,我想下载这个repo中的字典文件:https:// ...

  5. 网站内容禁止复制和粘贴、另存为的js代码

    1.使右键和复制失效 方法1: 在网页中加入以下代码: 复制代码代码如下: <script language="Javascript"> document.oncont ...

  6. SOFA 源码分析 — 链路数据透传

    前言 SOFA-RPC 支持数据链路透传功能,官方解释: 链路数据透传功能支持应用向调用上下文中存放数据,达到整个链路上的应用都可以操作该数据. 使用方式如下,可分别向链路的 request 和 re ...

  7. node七-required、缓存

    学会查API,远比会几个API更重要. 核心模块意义 -如果只是在服务器运行javascript代码,并没有多大意义,因为无法实现任何功能>读写文件.访问网络 -Node的用处在于它本身还提供可 ...

  8. tomcat7性能调优与配置(以windows版为例)

    一.配置tomcat服务状态查看帐号(E:\Tomcats\apache-tomcat-7.0.73Test\conf下面的tomcat-users.xml中)加入:<user username ...

  9. 洛谷 P1057 解题报告

    P1057 传球游戏 题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹 ...

  10. 杨老师课堂_Java核心技术下之控制台模拟记事本案例

    预览效果图: 背景介绍: 编写一个模拟记事本的程序通过在控制台输入指令,实现在本地新建文件打开文件和修改文件等功能. 要求在程序中: 用户输入指令1代表"新建文件",此时可以从控制 ...