《深入理解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. 【开源】OSharp框架解说系列(1):总体设计及系列导航

    系列文章导航 [开源]OSharp框架解说系列(1):总体设计 [开源]OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构 [开源]OSharp框架解说系列(2.2):EasyU ...

  2. MongoDB学习系列(2)--使用PHP访问MongoDB

    第一部分:介绍 在Windows上安装最新MongoDB步骤非常的简单,这里不做介绍.但是如果你安装的时候没有将MongoDB作为服务运行,每次你都要使用cmd切换到指定的目录下,然后在cmd中启动M ...

  3. Util应用程序框架公共操作类(五):异常公共操作类

    任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务上的错误进行简单支持. 对于刚刚接触.Net的新手,碰到错误的时候,一般喜欢通过返回bool值的方式指示是否执行成功. public boo ...

  4. 在web上逐行输出较大的txt文件

    在某些场景下,需要在web上展示一些日志文件,这些日志文件是放在文件服务器上的一些txt. 当日志文件很大时,下载日志会导致页面长时间卡住,一直在loading状态,而且下载完日志之后分析日志并生成d ...

  5. 小菜学习设计模式(一)—模板方法(Template)模式

    前言 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Method) ...

  6. spring源码分析之cache注解

    Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如EHCache 或者 OSCache),而是一个对缓存使用的抽象 ...

  7. Oracle配置和使用闪回

    环境:RHEL 6.4 + Oracle 11.2.0.4 目录: 一.闪回查询 1.1 闪回查询举例 1.2 闪回版本查询举例 二.闪回事物 2.1 闪回事物查询的先决条件 2.2 闪回事物查询 三 ...

  8. hibernate笔记--单(双)向的多对多映射关系

    在讲单向的多对多的映射关系的案例时,我们假设我们有两张表,一张角色表Role,一张权限表Function,我们知道一个角色或者说一个用户,可能有多个操作权限,而一种操作权限同时被多个用户所拥有,假如我 ...

  9. 根据起止日期构建指定查询条件:第N周(yyyy-MM-dd/yyyy-MM-dd)

    项目中有个查询模块中用到查询条件: 年和周. 以往我直接指定是第几周,后来测试反映如果直接选择周的话并不知道所选周代表的年月日,而无法最快查询数据,后更改查询条件如下: 指定一个起始年月,根据起始年月 ...

  10. 在ASP.NET Core使用Middleware模拟Custom Error Page功能

    一.使用场景 在传统的ASP.NET MVC中,我们可以使用HandleErrorAttribute特性来具体指定如何处理Action抛出的异常.只要某个Action设置了HandleErrorAtt ...