《深入理解Java虚拟机》第二三章摘要

Java内存区域与内存溢出

Java虚拟机中的内存分配图:

各个区域的特性总结如下表:

补充说明:

  • 当多线程情形下,可能多个线程要在堆上分配内存,那么可能出现内存分配的同步问题,解决方案有两个,一个就是同步内存分配动作;另一个就是采用TLAB,即在Java堆中针对每个线程先预先分配一小块线程私有的本地线程分配缓冲。这样当线程需要分配内存时就在自己的TLAB上进行,从而避免同步的开销。但是当TLAB分配满重新分配TLAB时仍需要同步;
  • 判断一个类是否属于无用的类,“可以”被回收的条件为:1 该类所有的实例都已经被回收;2 加载该类的ClassLoader已经被回收; 3 该类对于的java.lang.Class对象没有在任何地方被引用。注意,只是可以,而不是要被回收。

内存分配方式:虚拟机使用哪种方式由内存是否规整决定,而内存是否规整则由回收算法决定。

  1. 指针碰撞:假设所有已经被分配的内存放在一边,而空闲的放在另外一边,二者中间则用一个指针来作为分界点,当需要分配内存时只需要移动该指针即可,这样的方式称为指针碰撞。使用此方法的有Serial、ParNew等带Compact的回收器
  2. 空闲列表:虚拟机中已分配的内存和空闲的内存并不规整,处于相互交错的情形,那么就需要通过维护一个列表来表示哪些内存是可用,当需要分配内存时则需要通过查询列表来查找可用大小的内存。这样的方式称为空闲列表。使用此方法的有CMS等这种基于Mark-Sweep算法的回收器

对象在HotSpot虚拟机中的内存布局如下表所示:

在Java规范中,对于reference类型只规定了一个指向对象的引用,但没有规定通过何种方式去访问引用的数据。因此对于不同的虚拟机也有不同的访问方式,主要有两种方式:

  1. 使用句柄:在Java堆中划出一块区域作为句柄池来存储句柄,一个句柄包括对象实例数据的指针以及对象数据类型的指针,reference中存储的就是对象的句柄的地址。reference通过句柄来间接访问对象。其好处在于:当对象移动时,只需要改动矩形中的指针即可,而相应的reference则不需要做变动;
  2. 直接访问:reference存储的是对象地址,通过reference就可以直接访问数据。Java对中的数据对象中含有到对象数据类型的指针,比如上面提到的类型指针。此种方式的好处就是访问速度快,相比使用句柄的方式而言少了一次指针定位的开销。HotSpotVM使用的是此种方式。

两种使用方式的图示说明如下图:图片来源 http://www.th7.cn/Program/java/201604/846729.shtml

垃圾回收算法

判断一个对象是否死去,不可能再被用的算法有两种:

  1. 引用计数算法:对于一个对象,其被引用的次数增加一次,则计数加1,当引用失效的时候就减1.当其计数为0时,则认为该对象死去。该算法的特点是效率高,但是很难解决对象之间的相互引用问题。使用此算法的有MS的COM技术以及Python等
  2. 可达性分析算法:该算法的核心就是从GC Roots开始,检测所引用的所有对象。若一个对象到GC Roots之间没有任何引用链,那么认为该对象已死。使用此算法的有Java、C#等。

可以作为GC Roots的对象有:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • Native方法中引用的对象

在Java中有四种引用强度:

  • 强引用:强引用永远不会被垃圾回收器回收
  • 软引用:系统提供SoftReference类来表示,表示还有但是非必须的对象。其回收时机在于把若引用对象回收完之后内存还不够,则回收此类引用,若还不够,则OOM
  • 弱引用:通过WeakReference类来表示,此类引用只能生存到下一次垃圾回收之前。当垃圾收集器工作时,无论当前内存是否足够都会回收掉只被弱引用关联的对象。即只要发生了GC,弱引用必定被回收。其与已经没有到GC Roots的引用链的区别在于:还是可以通过弱引用来访问这些对象的,但是没有引用链的对象则永远不可能再被访问到了。
  • 虚引用:通过PhantomReference来实现,其对对象的生存时间没有任何影响,其存在的意义在于能在被虚引用引用的对象被回收的时候收到一个系统通知。

系统的GC工作流程如下图所示,总的来说,一个对象被回收可能会经过两次标记过程,并且可能在finalize方法中拯救自己以避免被回收。

几种典型的垃圾回收算法:

HotSpot VM中的垃圾回收算法的具体实现细节:为了结果的准确,GC在扫描时是需要冻结所有线程的。目前主流的Java虚拟采取的都是准确式GC,即系统知道每个内存位置的数据到底是一个什么数据类型,以HotSpot为例,它采取的是一个叫做OopMap的数据结构来实现这样的映射记录。有了这样的信息,虚拟机就直接知道哪些地方存放着对象的引用,从而避免了对内存挨个的检查,加快了GC扫描的速度。程序的每条指令都可能导致引用关系或者内存数据的变化,即会导致OopMap的变化,这样的话,如果给每个指令都生成一个对应的OopMap数据那么是相当占用空间的,于是提出了安全点的概念(SafePoint),即只有当每个线程都运行到线程对应的安全点时才进行GC扫描,从而也只要给安全点上的指令生成OopMap即可,这样就减少了OopMap的数量。而安全点的选取要考虑到GC频率与系统性能的综合影响,一般选取方法调用、循环跳转、异常跳转等“具有让程序长时间运行的特征”的点。为了让线程都跑到安全点停顿下来以进行GC扫描,有抢先式中断和主动式中断两种方式。这里又有另外一个问题,如果遇到一个比如处于Sleep状态的线程,那么它是不会走动的,如果它恰好不是在一个安全点Sleep,那么意味着它永远不会走到安全点来,所以又提出了安全区域(SafeRegion)的概念。即在这个区域内的点都是安全点。线程进入安全点之后会标志自己进入了安全区域,且必须等GC执行完了才会离开安全区域。

各个垃圾回收器:

几条最普遍的内存分配规则:

  • 对象优先分配在Eden区:当Eden区内存不够的时候,系统将发起一次速度较快的Minor GC
  • 大对象直接进入老年代:对于较长的数组以及字符串等大对象,直接把他们分配在老年代区。因此,对于那种生命周期较短的大对象,很容引起GC,应该尽量避免。
  • 长期存活的对象将进入老年代:一个若在Survivor区经历多次Minor GC还存活,默认15次,则将被移入老年代区。
  • 动态对象年龄判定:此条是结合上一条的,要是Survivor中相同年龄的所有对象的大小和超过Survivor的一半,那么年龄大于或者等于该年龄的对象将移入老年代区,无需等到15次
  • 空间分配担保:针对新生代的复制收集算法。若参数容许,当执行Minor GC之后,若存活的对象无法全部放在Survivor中,那么将把多的对象直接放入老年代区。若老年代也没足够的空间,那么将发生Full GC以获得更多空间

Java内存以及GC的更多相关文章

  1. java 内存回收(GC)的方式

    java内存的管理其实就是对象内存的管理,其中包括对象的分配和释放 对应程序员来说分配对象使用new关键字,而释放一个对象只需要让它等于null,让程序不能再访问这个对象,这时对象是不可达的,GC负责 ...

  2. 浅谈Java内存及GC

    目录: 1.JAVA虚拟机规范与JAVA虚拟机 2.JVM结构图 3.GC策略与原理 4.垃圾收集算法 5.回收的时机 6.垃圾搜集器 一.JAVA虚拟机规范与JAVA虚拟机 内存,是指程序运行时的数 ...

  3. Java 内存区域和GC机制分析

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  4. Java 内存区域和GC机制

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  5. Java系列笔记(3) - Java 内存区域和GC机制

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  6. 【转载】Java系列笔记(3) - Java 内存区域和GC机制

    Java系列笔记(3) - Java 内存区域和GC机制 转载:原文地址http://www.cnblogs.com/zhguang/p/3257367.html 目录 Java垃圾回收概况 Java ...

  7. Java内存分配以及GC

    转自http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html  写的太棒了,简单易懂 Java垃圾回收概况 Java GC(Gar ...

  8. 转载:Java 内存区域和GC机制

    原文链接:http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html 目录 Java垃圾回收概况 Java内存区域 Java对象的访 ...

  9. Java内存区域划分和GC机制

    Java 内存区域和GC机制   目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Ga ...

随机推荐

  1. 【原创】开源.NET排列组合组件KwCombinatorics使用(二)——排列生成

           本博客所有文章分类的总目录:本博客博文总目录-实时更新 本博客其他.NET开源项目文章目录:[目录]本博客其他.NET开源项目文章目录 KwCombinatorics组件文章目录: 1. ...

  2. 【原创】开源Math.NET基础数学类库使用(01)综合介绍

                   本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新  开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...

  3. Util应用程序框架公共操作类(十):可空值类型扩展

    当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供 ...

  4. 使用Free Spire.XLS插入图表

    使用Free Spire.XLS插入图表 前言 最近在研究Office中间件,上网搜索了下,比较出名的有两个:Aspose和Spire,两者功能齐全,对Office的支持趋近完善,但售价不菲.仔细搜索 ...

  5. 实现tap的多种方式

    一.tap.js 这是一个比较轻量的插件tap.js,142行代码,支持模块化开发. 1)handleEvent addEventListener方法中的第二个参数,我原先并没有注意到其实可以传一个对 ...

  6. Oracle丢失重做日志的几种场景恢复

    实验环境:RHEL6.4 + Oracle 11.2.0.4 一.丢失重做日志组中成员 1.1 故障模拟 1.2 处理方法 1.3 实际处理过程 二.丢失重做日志组 2.1 丢失INACTIVE重做日 ...

  7. Win8.1 安装Express 框架

    1.安装Windows Node.js客户端 2.安装Express框架 我本机是Win8.1的,使用命令npm install -g express安装Express,安装完成后显示一些安装明细,刚 ...

  8. Ftp上传下载 C#

    public class MyFtpClass { private readonly string _destIp; private readonly string _userName; privat ...

  9. VMware Workstation 10.0 正式版官方简体中文下载(附序列号)

    虚拟机界数一数二的王者软件VMWare Workstation 今日推出了最新的VMware Workstation 10.0 版本.该版本最大的更新是加入了简体中文语言,这意味着未来神马汉化包.中文 ...

  10. 几款主流PHP框架的优缺点评比

    PHP是一种在国内外都比较流行的开源服务器端脚本开发语言.能够适应大中小型项目的开发需求.我们将在这篇文章中向大家介绍几款主流PHP框架及其相关优缺点评比,作为一个参考分享给朋友们. 主要参考的PHP ...