热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java虚拟机的内存模型及基本算法

目录

一、JVM内存模型

1.1、JVM可划分为以下五个区

1.2、Eden空间和Survivor空间

1.3、内存分配及回收策略

二、JVM中的一些基本算法

2.1、对象存活判定算法

2.2、垃圾收集算法

2.3、HotSpot的算法实现

2.4、对象引用


一、JVM内存模型

1.1、JVM可划分为以下五个区

程序计数器

    可看作当前线程所执行的字节码的行号计数器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。

Java虚拟机栈

    用来描述java方法的执行。每个方法执行时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法接口等信息。方法的调用至完成的过程,对应着栈帧在虚拟机栈中入栈到出栈的过程。

本地方法栈

    与虚拟机栈功能类似,只是本地方法栈是为Native方法服务。在有的虚拟机中(如:Sun HotSpt虚拟机),Java虚拟机栈与本地方法栈合二为一。

堆(GC堆)

    用于存放对象实例,占据JVM所管理内存中的最大一块。可细分为新生代(Yong Gen)、老年带(Tenured Gen),新生代又可细分为Eden空间、From survivor空间、To Survivor空间。Eden空间和survivor空间默认比例为8:1。

方法区(Non-Heap 非堆)

    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(JIT)编译后的代码等数据。最初设计时使用的是永久代(Perm Gen)实现方法区,jdk1.8以后永久代被彻底移除。

  • 以上五个数据区中,方法区与堆为线程共享的,其余数据区为线程隔离的。
  • 方法区含有运行时常量池。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成的各种字面量和符号引用的常量池,这部分内容在类加载后进入方法区的运行时常量池。

1.2Eden空间和Survivor空间

    GC开始前对象只存于Eden空间和From Survivor空间,To Survivor空间是空的。GC时,Eden区中所有存活对象复制到To Survivor,年龄记为一岁,。From Survivor中存活对象计算年龄决定是否移至老年代。经过这次GC后,From和To空间角色交换,即原先的From变为To,To变为From。

1.3内存分配及回收策略

  1.  对象优先在Eden空间分配
  2. 大对象直接进入老年代
  3. 长期存活的对象将进入老年代。(默认15岁,每活过一个GC加一岁)

JVM中的一些基本算法

2.1对象存活判定算法

引用计数算法

给对象中添加一个引用计数器,每当一个地方引用它时,计数器值加1;当引用失效时,计数器值减1;任何时刻计数器为0的的对象就是不可能再被使用的。

引用计数算法的实现简单,判定效率也高,但难以解决对象间的循环引用问题。如:对象A中含有B的引用,对象B中含有A的引用,这样即使程序中不存在对象A、B的引用了,计数器人不为1,即无法进行垃圾回收。

可达性分析算法

主流商用程序语言的主流判定对象存活的算法实现。这个算法的基本思路就是通过一系列的称"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径车位引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。如下图(深入理解java虚拟机书中原图),对象object5、object6、object7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们被判断为可回收。

Java虚拟机的内存模型及基本算法

在java语言中,可作为GC Roots的对象包括下面几种:

 

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

2.2垃圾收集算法

标记 - 清除算法

标记-清除算法是最基础的收集算法。算法分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后同一回收所有标记的对象。

它主要不足有两个:

 

  • 效率问题,标记和清除两个过程效率都不高;
  • 空间问题,标记清除后会产生大量不连续的内存碎片(可能导致分配较大对象时找不到连续内存而不得不提前触发另一个垃圾收集动作)

复制算法

将可用内存分为大小相等的两块,每次只用其中一块。当这一块内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。

复制算法使得每次都是对整个半区进行内存回收,内存分配时再也不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。代价是将内存缩小为原来的一半,代价过高。

现在的商业虚拟机都采用这种收集算法来回收新生代。因为新生代中的对象98%是"朝生夕死"的,所以不需要1:1分配。只需分为一块较大的Eden空间和两块较小的Survivor空间即可(复制过程参考上文中Eden空间和Survivor空间的描述)。当然,当Survivor空间不够时,需要依赖老年代进行分配担保。

标记 - 整理算法

标记过程与"标记 - 清除"算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象移向一端,然后直接清理掉端边界以外的内存。该算法用于老年代。

2.3HotSpot的算法实现

HotSpot实现上述对象存活判定算法和垃圾收集算法时需要严格的考量,才能保证虚拟机的高效运行。在此,不展开说明,仅列举一些要点

枚举根节点:在安全点使用一组OopMap的数据结构记录哪些位置是对象的引用。

安全点:在安全点才能停顿进行GC,安全点是以"是否具有让程序长时间执行的特征"为标准选取的。如:方法调用、循环跳转、异常跳转等。

在GC发生时需要让所有线程都跑到最近的安全点上再停顿下来,有两种方案可供选择

 

  • 抢先式中断:在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复线程,让它跑到安全点上。现在几乎没有虚拟机采用这种方案。
  • 主动式中断:不对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点的地方是重合的。

安全区域:指在一段代码片中,引用关系不会发生变化,在这个区域中任意地方开始CG都是安全的。可看作被拓展的Safepoint,线程执行到Safe Region时,会标识自己进入Safe Region,这样,GC发生时,虚拟机就不用管为自己标识了Safe Region状态的线程了。

2.4对象引用

在前面对象存活判定中,对象是否存活都与引用有关。那么引用到底是什么?

JDK 1.2之后,Java将引用分为强引用 (Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种。

 

  • 强引用:类似"Object obj = new Object()"这类引用,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象。
  • 软引用:用来描述一些还有用但并非必须的对象。对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。
  • 弱引用:也是用来描述非必需对象,但它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前
  • 虚引用:也被称为幽灵引用或幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用获取一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

 

注:本文实则为《深入理解Java虚拟机》的读书笔记,文中概念大多引自该书。


推荐阅读
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • Mono为何能跨平台
    概念JIT编译(JITcompilation),运行时需要代码时,将Microsoft中间语言(MSIL)转换为机器码的编译。CLR(CommonLa ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • Android系统启动过程分析一、Android平台架构首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知。可以看出整 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
  • Java面试题系列:将面试题中比较经典和核心的内容写成系列文章持续在公众号更新,可巩固基础知识,可梳理底层原理,欢迎大家持续关注【程序新视界】。本篇为面试题系列第2篇。常见面试问 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • 子类从父类继承所有的成员(字段,方法,嵌套类),构造方法不属于成员,所有子类不能继承,但是子类可以调用父类的构造方法对于private方法和属性,子类一定是继承了的,但是没有访问权 ... [详细]
  • 这篇文章主要讲解了“如何应对Android面试”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何应对 ... [详细]
  • java中的try catch_Java中的trycatchfinally异常处理
    Java中的try-catch-finally异常处理一、异常处理异常(Exception):是在运行发生的不正常情况。原始异常处理:if(条件){处理办法1处理办法 ... [详细]
author-avatar
细妹很快乐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有