Java垃圾回收机制 入门

时间:2022-09-16 17:02:03

对于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. Git 常用命令详解

    Git 是一个很强大的分布式版本管理工具,它不但适用于管理大型开源软件的源代码(如:linux kernel),管理私人的文档和源代码也有很多优势(如:wsi-lgame-pro) Git 的更多介绍 ...

  2. oracle连接数据的oci和thin的区别

    1)从使用上来说,oci必须在客户机上安装oracle客户端或才能连接,而thin就不需要,因此从使用上来讲thin还是更加方便,这也是thin比较常见的原因.2)原理上来看,thin是纯java实现 ...

  3. 已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数

      package homework002; import java.util.ArrayList; import java.util.List; import java.util.Random; p ...

  4. XCode模拟器屏幕显示内容非常慢

    今天遇到了一个问题,就是XCode模拟器屏幕显示的非常慢,但是点击一下模拟器,内容就会马上显示出来,最后查找出问题是自己不注意把刷新UI界面的代码放到了异步线程中去执行了,刷新UI界面的代码放到主线程 ...

  5. Java中的I/O流

    import java.io.*//生成代表输入流的对象fis=new FileInputStream("e:/src/from.txt") //生成代表输出流的对象 fos=ne ...

  6. mac下配置java环境

    1.tomcat配置 http://www.cnblogs.com/freeyiyi1993/p/3436368.html 2.下载eclipse和jdk安装  jdk去oracle网站下载

  7. cmd命令行查看当前系统版本和版本是32位还是64位

  8. 帝国CMS系统结合项图文教程

    为了使信息列表可实现按多种条件输出数据,帝国CMS独创可设置无限条件的模型结合项功能.帝国CMS的结合项功能是指按模型多个字段内容来结合显示对应的信息. 二.结合项的语法说明 结合项访问地址: /e/ ...

  9. 【2017集美大学1412软工实践_助教博客】团队作业3——需求改进&系统设计 成绩公示

    第三次团队作业成绩公布 题目 团队作业3: 需求改进&系统设计 团队成绩 成绩公示如下: 缩写 TD BZ GJ CJ SI WBS GS JG DB SS SJ CS DC Total 分值 ...

  10. Mysql、Hbuilder、Idea快捷键

    MyEclipse 快捷键 ↑ ↓ ← →多 1.方法抽取,Alt+Shift+M 2.多行注释:Ctrl+Shift+/ 3.对象.方法; Ctrl+2 + ↓+回车 ,自动生成返回类型和变量 (非 ...