对于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. 为什么SqlTransaction.Rollback会抛出SqlException(11,-2)(即SQL超时异常)

    // System.Data.SqlClient.SqlTransaction public override void Rollback() {     if (this.IsYukonPartia ...

  2. install cx_Oracle on Linux

    step 1 : install oracle client library url: http://www.oracle.com/technetwork/topics/linuxsoft-08280 ...

  3. 让VS 2010在调试字符串时,支持Json数据格式友好显示

    阅读本文如果对Microsoft.VisualStudio.DebuggerVisualizers的用法不熟悉的,可以参考这篇文章.http://www.cnblogs.com/devil0153/a ...

  4. python2.7.9基础学习

     一.  基 础 python python开头两行注释代码意义:         #!/usr/bin/python 是用来说明脚本语言是python的,是要用/usr/bin下面的程序(工具)py ...

  5. 更换win7锁屏壁纸

    1.打开注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Backgroun ...

  6. 使用Fluent配置表关系

    转载MS官方文档:https://msdn.microsoft.com/zh-cn/data/jj591620 Configuring Relationships with the Fluent AP ...

  7. Android Activity 四种启动模式

    task和back stack(任务和回退栈) 任务启动,task被加入到回退栈的栈顶,返回的时候回退栈的栈顶任务会被弹出,并被销毁,栈中的前一任务恢复运行,当activity销毁是,系统不会保留ac ...

  8. SQL Server附加数据库出现错误5123的正确解决方法

    因为自己有一本基于SQL Server 2005的数据库教程,里边使用的示例数据库是AdventureWorks for SQL Server 2005,而我的机子上装的是SQL Server 200 ...

  9. C# 禁止程序多个实例运行

    //program.cs    static class Program    {        /// <summary>        /// 应用程序的主入口点.        // ...

  10. .net MVC 中枚举类型Enum 转化成 下拉列表的数据源

    第一次写技术博文,记录下工作中遇到的问题,给自己的知识做个备份,也希望能帮助到其他的同学 最近接手了公司的一个新的项目.有个页面涉及相关设计. 分享一个经常用到的吧. 方法一: 直入主题吧 我们的目的 ...