对于Java虚拟机的了解,我认为是一个Java程序员已经入门的重要标志,而JVM中的垃圾回收机制(Garbage Collection,简称GC)又是JVM中的重点,所以hans在这里用篇文章时间和大家一起了解一下GC。

GC是Java平台中最重要的标志之一,它最早诞生于有MIT研发的一门叫做Lisp的语言之中,所以它的历史要比Java更悠久。在它广泛使用之前,程序员要经常处理由于内存处理不完善而引起的bug。但是即使在内存处理极其完善的今天,我们这些想成为优秀程序员的程序猿,也不能将它视为透明,因为当垃圾收集成为系统达到更高并发量的瓶颈的时候,我们必须会对它实施必要的监控和调节。

其实在垃圾回收机制刚刚诞生的时候,关于内存,人们就经常想3个问题:

  1. 哪些内存需要回收?
  2. 什么时候回收?
  3. 怎么回收?

这几个问题咱们一个个看。

先说哪些内存需要回收

众所周知,Java的内存大体可划分为方法区、栈(虚拟机栈和本地方法栈)、堆。因为栈是线程私有的,每一个栈中的内存如何分配,基本上在编译期就已知了,同时这部分内存随着线程或方法的结束就跟着回收了,所以栈中的内存分配和回收具有确定性。但堆和方法区就不一样了,由于具有“动态绑定”和“运行时产生新常量”等特性,所以这部分的内存是在运行期分配的,是不确定的,内存回收指的就是回收这一部分的内存。

知道了要回收的内存是堆中和方法区中的内存,现在需要知道就是“什么时候回收”了。(抢答————在调用System.gc()的时候回收。抢答正确!但如果就这么简单我会在这里另起一段?)

这里说的是“什么时候回收”确切的说是“内存处于什么状态时回收”(骂我表述不准确?我愿意,你来打我呀!)一般来讲大家的第一个反应是“有地方引用它就不回收、没地方引用就回收”,很好!这是一个很棒的思路(Python的垃圾回收就是用的这种思路)但是你细想想这种思路有一个比较致命的问题,想到是什么了吗?没想到就看看下面的代码吧。

public class Test {
public Test instace = null;
public static void main(String[] args) {
Test testA = new Test();
Test testB = new Test();
testA.instace = testB;
testB.instace = testA; testA = null;
testB = null;
}
}

这种思路的致命问题就是它存在对象间循环引用的问题,像上面例子中的testA和testB已经无法访问了,理应被回收。但是由于它们互相引用着对方,所以它们有地方被引用,利用上述思路就无法回收。如何解决这种问题呢?

“可达性分析”就可以完美解决这种问题,可达性分析是通过一些“根”作为起点,将“根”能引用到的对象加入“可达对象集合”中,再讲其中的对象引用到的对象加入该集合中,重复这个过程直到没有任何对象可以加入该集合。我们得到的这个集合就是不用回收的对象集合,剩下的就都可以回收了。现在问题来了,“根”是什么呢?(我不会告诉你它是火之国木叶村的地下忍者组织。活跃一下气氛别太当真)“根”也是对象,包括下面几种:

  • 栈中所引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象

就如上面那个例子,testA和testB虽然都被别人引用,但是无法从“根”开始达到它们,所以“可达性分析”就很成功的解决了循环引用的问题,所以现在Java、C#等都用的是这种方式来确定哪些对象可以回收。

现在前两个问题解决了,开始解决第三个问题——怎么回收?

最简单的方法就是将可以回收的对象标记一下,然后召唤“对象回收程序”将它们回收,这种方法即简单又可行,但存在一个关键的问题(就知道不可能这么简单)那就是——碎片化。想象一下,内存是连续的,但可回收对象的位置是随机的,这样就导致了内存回收后,虽然有很大的内存空间,但是这些内存空间被一些“不可被回收的对象”分成了一个个小块,若重新整理一遍内存、清理碎片又要花费大量的时间。为了解决这个问题出现了两种优秀的解决办法分别是“复制法”和“标记-整理法”

先谈谈“标记-整理法”。这种方式其实将内存回收和内存碎片整理合二为一的方法,让两个工作同时进行,从而极大的减少了时间。它是在可以回收的对象标记之后,将不可回收的对象向内存一端移动,这个过程中直接可以将部分可回收的对象覆盖,当移动完成后,直接清空边界以外的内存。这样就极大的减少了回收后再进行碎片整理的时间。

再来看看“复制法”。这种方式的原形是将内存分为两块,分配对象时只在其中的“内存块1”上操作,当这一块用完了之后,将不可以被回收的对象复制到“内存块2”上去,然后将“内存块1”清空。这种方式会比上面那种方法还要快,但是存在一个不足,就是让分配对象时的内存缩小为原来的一半,这显然不是很合算。人们通过观察发现,新建立的对象会有很大一部分是可回收的,所以就有了一种更巧妙的改进版。

这种改进版就是将内存分成一块较大的Eden空间和两块较小的Survivor空间(以HotSpot为例,大小比例是8:1:1),每次使用Eden和一个Survivor。回收时,将两者中存活的对象复制到另一Survivor中,然后清空这两个内存空间。这样的话之会有10%的内存被浪费。通过这样的改进“复制法”的性能就是很可观的了。但是也有时会有大量的对象存活下来,导致Survivor空间不够用,这就使人们加深对对象存活的观察和思考。

观察发现虽然刚刚新建的对象“可被回收率”高达98%,但经过几轮回收后剩下来的对象,一般是经常要用到的,很少可以被回收。所以人们就想可以将Survivor空间中存活了几轮的对象,放到一个新的空间中,在这个空间每次回收的对象会相对较少,因此可以将回收频率调低。这一部分的回收方法自然要用到“标记-整理法”,并且将这部分称为“老年代”,将刚刚新建的对象称为“新生代”。“新生代”的内存回收用“复制法”而老年代用“标记-整理法”,这样的方式就是现在大部分虚拟机采用的“分代收集法”

关于GC的基本理论就讲到这里了,以后等我再有所长进,再跟大家深入的讨论。这篇文章可能有一些错误或不足,希望大家及时通知我会改的。

Java垃圾回收机制 入门的更多相关文章

  1. 【转载】Java垃圾回收机制

    原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...

  2. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  3. 深入理解java垃圾回收机制

    深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  4. Java垃圾回收机制_(转载)

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  5. 成为Java GC专家(3)—如何优化Java垃圾回收机制

    为什么需要优化GC 或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或 ...

  6. java 垃圾回收机制 引用类型

    Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对 ...

  7. 【Java】Java垃圾回收机制

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  8. Java垃圾回收机制的工作原理

    Java垃圾回收机制的工作原理 [博主]高瑞林 [博客地址]http://www.cnblogs.com/grl214 获取更多内容,请关注小编个人微信公众平台: 一.Java中引入垃圾回收机制的作用 ...

  9. Java 垃圾回收机制(早期版本)

    Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但 ...

随机推荐

  1. css3画三角形的原理

    以前用过css3画过下拉菜单里文字后面的“下拉三角符号”,类似于下面这张图片文字后面三角符号的效果 下面是一个很简单的向上的三角形代码 #triangle-up { width: 0; height: ...

  2. test spring in category

    test tile package com.journaldev.spring.controller; import java.text.DateFormat; import java.util.Da ...

  3. Maven如何添加Oracle驱动包到本地仓库中

    2016-05-28 http://note.youdao.com/yws/public/redirect/share?id=54cbe79c8948113ef492f946f7e027c8& ...

  4. MVC中使用内建的HTML辅助方法产生表单元素提交表单与button按钮事件的陷阱

    网站模板页有个登陆的退出按钮,当点击时跳转到登陆页面. <button onclick="logout()" >退出</button> $("#l ...

  5. Winmail.dat(TNEF) issue on outlook

    http://www.dwheeler.com/essays/microsoft-outlook-tnef.html

  6. Linux学习三:Ubuntu下使用minicom和开发板通信

    备注:如果你是用的是Windows则使用超级终端即可:开始-程序-附件-通讯-超级终端 现在我们在Ubuntu下安装配置minicom: 1.进入ubuntu桌面ctrl+alt+t打开终端 输入:s ...

  7. Javascript中判断变量是 array还是object(是数组还是对象)

    段文字是从github上截取由本人翻译过来的. 原文地址:https://github.com/nathansmith/javascript-quiz/blob/master/ANSWERS.md 怎 ...

  8. java的向上转型总结

    在<think of java>中对'向上转型'有着如下的描述 看完之后很蒙圈,所以自己在网上找了一些描述,并自己做了简单的总结 简单的例子 class A{ public void A1 ...

  9. 前端学习实践笔记--JavaScript深入【2】

    趁热继续再来学习一波,接下来主要介绍函数,object,数组,面向对象,new实例化. 在介绍“对象”之前,首先得梳妆打扮一番吧,那这梳妆打扮主要有两条路线,一条是淑女范(利用函数对象化),一条是邻家 ...

  10. 可变参数宏__VA_ARGS__和...

    __VA_ARGS__ 是一个可变参数的宏(gcc支持).实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点).这样预定义宏_ _VA_ARGS_ _就可以被用在替换部分中,替换省略号所 ...