资讯专栏INFORMATION COLUMN

JAVA虚拟机

edagarli / 1104人阅读

摘要:它一般运行在模式下的虚拟机。设置最大垃圾收集停顿时间设置吞吐量大小开关参数,打开以后就由虚拟机自动调节策略。

Java内存区域

    程序计数器:当前线程所执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令

    虚拟机栈:指我们平常所说的堆栈中的栈(或者说是虚拟机栈中的局部变量部分),线程私有的,生命周期与线程相同。用来描述Java方法执行的内存模型,用于存储局部变量表、操作栈、动态链接、方法出口等信息

    本地方法栈:跟虚拟机栈类似,不过本地方法栈是为虚拟机使用到的Native方法服务

    堆:被所有线程共享的一块内存区域,用来存放对象实例以及对象类型数据的地址信息,如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

    方法区:跟堆一样,是各个线程共享的内存区域,用来存储类信息(对象类型、父类、实现的接口、方法等)、常量、静态变量,其中方法区还包含一个运行时常量池,用来存储编译期生成的各种字面量和符号引用

垃圾收集器及内存分配策略

对象存活判断

垃圾回收主要是回收堆内存。在垃圾回收期(GC)回收之前,需要确定哪些对象可以回收,有以下几种方法:

    引用计数算法
    原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。这种算法效率高。不过很难解决对象之间的相互循环引用的问题。

    根搜索算法(默认)
    原理:通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。作为GC Roots的对象包括以下几种:

    虚拟机栈中的引用的对象

    方法区中的类静态属性引用的对象

    方法区中的常量引用的对象

    本地方法栈中JNI的引用的对象

引用

强引用,类似"Object obj = new Object()"这种,只要强引用存在,则GC永远不会回收被引用的对象

软引用,指还有用,但是并非必须的对象,内存溢出之前进行回收,实现软引用可以通过SoftReference类,软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

弱引用,跟软引用一样,不过强度比软引用弱一些,第二次垃圾回收时回收,实现弱引用可以通过WeakReference类,弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用,是最弱的一种引用关系,垃圾回收时回收,无法通过引用取到对象值。主要用于检测对象是否已经从内存中删除。

垃圾收集算法

    标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,不过该算法有以下缺点:

    效率低

    空间问题,该算法会产生大量不连续的内存碎片,这样导致程序在以后的运行中如果需要分配较大对象时无法找到足够的连续内存而触发另一次垃圾收集动作

    复制算法:将可用内存按容量划分大小相等的两块,每次只使用其中的一块。当一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这种算法实现简单,效率高,不过会将可使用的内存减少一半。如果对象存活率高就要执行较多的复制操作,将导致效率变低。目前在复制算法中,通常是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次只使用Eden和一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另外一块Survivor中,最后清理使用的Eden和Survivor。并且以老年代作为空间分配担保,即Survivor无法容纳的对象会直接进入老年代。目前新生代主要采用这个算法。

    标记-整理算法:将所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

    分代收集算法:根据对象的存活周期的不同将内存划分为几块,一般是分为新生代和老年代。然后根据各个年代的特点采用最适当的收集算法。新生代通常采用复制算法,因为对象生存时间都不长。老年代一般采用"标记-清理"或者"标记-整理"算法回收,因为老年代中对象存活率高,没有额外空间对它进行分配担保。

垃圾收集器

    Serial收集器:这是一个单线程的收集器,该收集器在进行垃圾收集时,必须暂停其它所有的工作线程,不过该收集器简单而高效。它一般运行在Client模式下的虚拟机。新生代收集器。

    ParNew收集器:这是一个多线程版本的Serial收集器。新生代收集器。

    Parallel Scavenge收集器:新生代收集器,使用复制算法,并行多线程。它主要是控制吞吐量=(运行用户代码时间)/(运行用户代码时间+垃圾收集时间)。有两个参数可以用来精确控制吞吐量。

    -XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间

    -XX:GCTimeRatio:设置吞吐量大小

    -XX:+UseAdaptiveSizePolicy:开关参数,打开以后就由虚拟机自动调节策略。这也是跟ParNew收集器的一个重要区别

    Serial Old收集器:这是Serial收集器的老年代版本,使用"标记-整理"算法,该收集器有两大用途,一是与Parallel Scavenge收集器搭配使用。二是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用。

    Parallel Old收集器:是Parallel Scavenge收集器的老年代版本。使用"标记-整理"算法。

    CMS收集器:以获取最短回收停顿时间为目标的收集器。重视服务的响应速度。主要用于互联网或B/S系统的服务端上。基于"标记-清除"算法。老年代收集器。使用CMS收集器时,不能像其它收集器那样等到老年代几乎被填满了再进行收集,需要预留一部分空间提供并发收集时的程勋运行使用。主要优点就是并发收集、低停顿。它有以下缺点:

    对CPU资源非常敏感

    无法处理浮动垃圾。即在垃圾收集阶段用户线程继续进行,这个过程会有新的垃圾产生,但是CMS收集器不会对这部分垃圾(浮动垃圾)进行标记。如果预留的内存空间不足,就会导致Concurrent Mode Failure。这时会临时启动Serial Old收集器重新进行老年代的垃圾收集,导致停顿时间过长。

    由于采用的算法,会导致收集结束时产生大量空间碎片。

    G1收集器:基于"标记-整理"算法实现。可以非常精确的控制停顿,并且在不牺牲吞吐量的前提下完成低停顿的内存回收。因为该收集器会将整个java堆划分为多个大小固定的独立区域,并且跟踪这些区域的垃圾堆积程度,每次根据允许的收集时间,优先回收垃圾最多的区域。 垃圾收集相关的常用参数参见下图:

内存分配

    对象首先在新生代Eden区分配,当Eden没有足够的空间进行分配时,虚拟机将发起一次MinorGC

    大对象直接进入老年代,通过-XX:PretenureSizeThreshold参数区分,大于这个值的表示大对象,该参数只对Serial和ParNew有效

    长期存活的对象将进入老年代,识别对象长期存活通过参数-XX:MaxTenuringThreshold设定,对于每个对象而言,虚拟机都会定义一个年龄计数器,如果对象在经过第一次MinorGC后仍然存活,并且能够被Survivor容纳的话,就将被移动到Survivor中,并且年龄增加一岁。每经过一次MinorGC,年龄就增加一岁,超过设定值或者默认值(15),就会进入老年代中。

名词解释

    MinorGC:指发生在新生代的垃圾收集动作,频繁,速度快

    MajorGC/Full GC:指发生在老年代的GC,速度慢

    大对象:需要大量连续内存空间的Java对象,比如很长的字符串及数组,应当避免短命的大对象

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7200.html

相关文章

  • 深入理解Java虚拟到底是什么

    摘要:由虚拟机加载的类,被加载到虚拟机内存中之后,虚拟机会读取并执行它里面存在的字节码指令。虚拟机中执行字节码指令的部分叫做执行引擎。 什么是Java虚拟机? 作为一个Java程序员,我们每天都在写Java代码,我们写的代码都是在一个叫做Java虚拟机的东西上执行的。但是如果要问什么是虚拟机,恐怕很多人就会模棱两可了。在本文中,我会写下我对虚拟机的理解。因为能力所限,可能有些地方描述的不够欠...

    宋华 评论0 收藏0
  • 《深入理解Java虚拟》(一)Java虚拟发展史

    摘要:虚拟机发展史注本文大部分摘自深入理解虚拟机第二版作为一名开发人员,不能局限于语言规范,更需要对虚拟机规范有所了解。虚拟机规范有多种实现,其中是和中所带的虚拟机,也是目前使用范围最广的虚拟机。世界第一款商用虚拟机。号称世界上最快的虚拟机。 Java虚拟机发展史 注:本文大部分摘自《深入理解Java虚拟机(第二版)》 作为一名Java开发人员,不能局限于Java语言规范,更需要对Java虚...

    张春雷 评论0 收藏0
  • 《深入理解Java虚拟》(二)Java虚拟运行时数据区

    摘要:虚拟机运行时数据区分为以下几个部分。程序计数器也是在虚拟机规范中唯一没有规定任何异常情况的区域。在方法运行期间不会改变局部变量表的大小。长度在位和位的虚拟机中,分别为官方称它为。 Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 《深入理解Java虚拟机》第二章 ,为了整理思路,简单记录一下,方便后期查阅。 2.2 运行时数据区域 Java虚拟机在Java程序运行时...

    draveness 评论0 收藏0
  • Java虚拟规范(介绍)

    摘要:此处指定的虚拟机与平台兼容,并支持语言规范中指定的编程语言。第章说明了虚拟机的指令集,按字母顺序显示操作码助记符。 介绍 一点历史 Java®编程语言是一种通用的、并发的、面向对象的语言,它的语法类似于C和C++,但它省略了许多使C和C++复杂、混乱和不安全的特性。最初开发Java平台是为了解决为联网的消费者设备构建软件的问题,它旨在支持多种主机架构,并允许安全交付软件组件,为了满足这...

    chnmagnus 评论0 收藏0
  • 【JVM从小白学成大佬】2.Java虚拟运行时数据区

    摘要:虚拟机在执行程序的过程中会把它所管理的内存划分为若干个不同的数据区域。栈帧栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧的概念结构如下运行时数据区脑图高 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题。但是,往往都会令我比较尴尬,我还话音未落,面试者就会背诵一段(Java虚拟...

    shuibo 评论0 收藏0
  • Java虚拟规范(Java虚拟的结构)

    摘要:原始类型和值虚拟机支持的原始数据类型是数字类型布尔类型和类型。,其值为位带符号的二进制补码整数,其默认值为零。 Java虚拟机的结构 本文档指定了一个抽象机器,它没有描述Java虚拟机的任何特定实现。 要正确实现Java虚拟机,你只需要能够读取类文件格式并正确执行其中指定的操作,不属于Java虚拟机规范的实现细节会不必要地限制实现者的创造力。例如,运行时数据区的内存布局、使用的垃圾收集...

    bang590 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<