首页 > 其他分享 >JDK、JRE、JVM三者之间的关系?

JDK、JRE、JVM三者之间的关系?

时间:2022-12-21 00:44:18浏览次数:46  
标签:JRE JDK 收集器 标记 内存 回收 垃圾 JVM 加载

 

JDK安装目录下有一个【jre】目录(bin->【jvm】, lib->jvm工作所需类库)

JVM体系结构与运行原理

 

JDK基础概念及目录结构

JDK指的就是Java开发工具包。我们依靠JDK开发和运行Java程序。包含了JRE 和 开发者工具(编译器(javac[.exe])、调试器、javadoc等)

JRE为Java提供了运行环境,通过JVM将字节码解释称可执行的机器码。由JVM、Java运行时类库、动态链接库等组成

JVM可以看做一台抽象化的计算机,它有一套完整的体系架构,包括处理器、堆栈、寄存器等。

 

说下JVM的主要组成部分?及其作用

JVM主要有类加载器、字节码执行引擎、内存模型组成。

一个程序运行,启动main方法是由【类加载子系统】将需要执行的类.class字节码文件加载到【内存模型】的方法区,再由【字节码执行引擎】将main方法压入栈,栈又会要给执行的方法分配一个栈帧。

类加载器:class loader,加载类文件到内存,实现类的加载动作。 只要符合文件结构它只管加载,能否运行,它不负责。

执行引擎:exection engine,也叫解释器,负责解释命令,交由操作系统执行。

本地接口:native interface,融合不同的语言为java所用。

运行数据区:Runtimedata area,类加载器将程序加载到此处才开始运行。

(1)堆。堆是java对象的存储区域,任何用new字段分配的java对象实例和数组,都被分配在堆上,java堆可用-Xms和-Xmx进行内存控制,jdk1.7以后,运行时常量池从方法区移到了堆上。

(2)方法区:被所有线程共享的,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

  误区:方法区不等于永久代

  很多人把方法区称作“永久代”(Permanent Generation),本质上两者并不等价,只是HotSpot虚拟机垃圾回收器团队把GC分代收集扩展到了方法区,或者说是用来永久代来实现方法区而已,这样能省去专门为方法区编写内存管理的代码,但是在Jdk8也移除了“永久代”,使用Native Memory来实现方法区。

(3)虚拟机栈:虚拟机栈中执行每个方法的时候,都会创建一个栈桢用于存储局部变量表,操作数栈,动态链接,方法出口等信息。

(4)本地方法栈:与虚拟机发挥的作用相似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。

(5)程序计数器:每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎(解释器)读取下一条指令。指示Java虚拟机下一条需要执行的字节码指令。

说下JVM内存运行时区域?

JVM在执行Java代码时,会将内存分为几个部分即【数据区】来使用。

 

Java类文件结构

魔数:

每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件 是否为一个能被虚拟机接受的Class文件。

class文件版本:第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。

常量池:

紧接着主次版本号之后的是常量池入口,常量池可以理解为Class文件之中的资源仓库, 它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。

常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。

访问标志:

在常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用于识 别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类 型;是否定义为abstract类型;如果是类的话,是否被声明为final等。

当前类索引、父类索引、接口索引集合:

类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,接口索引集合(interfaces)是一组u2类型的数据的集合.

类索引 : 用于确定这个类的全限定名

父类索引 : 用于确定这个类的父类的全限定名。由于Java语言不允许多重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的Java 类都有父类,因此除了java.lang.Object外,所有Java类的父类索引都不为0。

接口索引集合 : 就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句

字段表集合:

用于描述接口或者类中声明的变量。

方法表集合

用于对方法的描述,与对字段的描述几乎采用了完全一致的方式。

属性表集合

在Class文件、字段表、方 法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。

类的生命周期

加载:将class文件从磁盘读到内存

验证:验证字节码文件的正确性

准备:给类的静态变量分配内存,并赋予默认值

解析:类装载器装入类所引用的其它所有类

初始化:为类的静态变量赋予正确的初始值(程序),执行静态代码块

使用:程序之间的调用

卸载:由gc进行,就是把垃圾从内存中回收的过程

类加载的执行过程?

首先,创建对象的类及其直接与间接父类

然后,加载类的同时,加载静态成员,主要包括静态成员变量的初始化,静态语句块的执行,按代码的先后顺序进行加载。

其次,需要的类加载完成后,开始创建对象,会先加载非静态成员,主要包括非静态成员变量的初始化,非静态语句块的执行,

按代码的先后顺序进行加载

最后,执行构造器,构造器执行完毕,对象生成

Java类加载的过程?

加载、连接(验证、准备、解析)、初始化

加载:

①通过类的全限定名获取定义此类的二进制字节流

②将字节流所代表的静态存储结构转化为方法区的运行时数据结构

③在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

一个非数组类的加载阶段(加载阶段中获取类的二进制字节流的动作)是可控性最强的阶段,我们还可以自定义类加载器去控制字节流的加载方式(即重写一个类加载器的loadClass()方法)。

数组类不通过类加载器创建,直接由Java虚拟机创建。

加载阶段和连接阶段的部分内容是交叉进行的,加载阶段尚未结束,连接阶段可能就已经开始了。

验证:

确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

①文件格式验证:验证字节流是否符合Class文件格式的规范,并能被当前版本的虚拟机理解。

保证输入的字节流能正确的解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求。

②元数据验证:校验类的元数据信息的语义。

保证不存在不符合Java语言贵方的元数据信息。

③字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。

保证被校验类的方法在运行时不会做出危害虚拟机安全的事件。

④符号引用验证:对类自身以外(常量池中的各种符号引用)的信息进行匹配校验

确保解析动作能正常执行

准备:

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。这些内存都将在方法区中分配。

注意:

①这时候进行内存分配的仅包括类变量(static),而不包括实例变量。实例变量会在对象实例化时随着对象分配在堆中

②初始值“通常情况”下是数据类型默认的零值(0,0L、0.0f、0.0d、null、false)

③final的变量会被正常赋值

解析:

负责找到二进制字节码并加载至JVM的【方法区】中;JVM通过类名、类所在包名通过ClassLoader完成类的加载

采用三个元素表示一个被加载了的类:类名+包名+ClassLoader实例ID。

连接:负责对二进制字节码的格式校验;初始化装载类中的静态变量、解析类中调用的接口;验证类中的所有属性、方法的存在

及权限。

初始化:执行类中静态初始化代码、构造器代码及静态属性的初始化。

调用new;反射调用了类中的方法;子类调用了初始化;JVM启动过程中指定的初始化类

类加载器种类

JVM中内置了三个重要的加载器,除了启动类加载器,其它加载器均由Java实现且全部继承自java.lang.ClassLoader

Bootstrap ClassLoader(启动类加载器):最顶层的加载类,负责加载%JAVA_HOME%/lib目录下的jar包和类(JRE核心类库),由C++实现。

Extension ClassLoader(扩展类加载器):负责加载%JRE_HOME%/lib/ext目录下的jar包和类

Application ClassLoader(应用程序类加载器):负责加载当前应用classpatch下的所有jar包和类

User ClassLoader(自定义类加载器):负责加载用户自定义路径下的包

类加载机制

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

什么是双亲委派模型?

双亲委派模型:

要求除顶层启动类加载器外其余类加载器都应有自己的父类加载器;类加载器直接通过复用关系来复用父加载器的代码。

工作工程:

如果一个类加载器收到了类加载请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类加载器反馈自己无法完成这个加载请求时(他的搜索范围中没有找到所需要的类),子加载器才会尝试自己去加载。

当Application ClassLoader收到一个类加载请求时,它首先不会自己去尝试加载这个类,而是委派给ExtensionClassLoader

当Extension ClassLoader收到一个雷佳在请求时,它首先也不会自己去尝试加载这个类,而是委派给Bootstrap ClassLoader,

如果Bootstrap ClassLoader加载失败(在\lib中未找到所需类),就会让Extension ClassLoader尝试加载,

如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载,

如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载,若均失败,就会抛出ClassNotFoundException.

 

实现过程:

实现双亲委派模型的代码都集中在java.lang.ClassLoader的loadClass()方法中:

首先会检查请求加载的类是否已被加载过;

加载过的类会直接返回,若没加载过,则递归调用父类加载器的loadClass();

父类加载器为空后,就是用启动类加载器Bootstrap ClassLoader加载;

若父类加载器和启动类加载器均无法加载,则调用自身的加载功能。

 

优点:

Java类随着它的类加载器一起具备了一种带有优先级的层级关系,确保了在各种加载环境的加载顺序。

保证了运行的安全性,防止不可信类扮演可信任的类。

 

ClassLoader抽象类的几个关键方法?

① loadClass

  此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parentClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如果要改变类的加载顺序,则可覆盖此方法。

② findLoadedClass

    此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。

③ findClass

    此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。

④ findSystemClass

    此方法负责从System ClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然未找到,则返回null。

⑤ defineClass

    此方法负责将二进制的字节码转换为Class对象。

⑥ resolveClass

此方法负责完成Class对象的链接,如已链接过,则会直接返回。

 

什么是GC,为什么要有GC,GC分类?

GC,Garbage Collection 是垃圾收集,负责清除JVM内存模型中存放的对象 并 释放内存,Java提供的GC功能可以自动检测对象是否超过作用域从而达到自动回收内存的目的,防止内存泄漏。

一个Java程序在执行中如果没有GC,那么运行过程中会产生很多无用且占据内存空间的对象、连接池、线程池、引用、变量等,如果没有GC处理机制,则需要我们自己想办法解决垃圾处理问题,不然内存开销过大就会导致内存溢出,导致整个程序崩溃。

Partial GC:并不收集整个GC堆的模式

Young GC(Minor GC):只收集young gen的GC

Old GC(Major GC):只收集old gen的GC。只有CMS的concurrent collection是这个模式

Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式

Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

JVM垃圾回收?

平时的开发中,有时我们需要创建大量的对象,如果我们动态创建的对象没有得到及时回收。持续堆积,最后会导致内存被占满,造成溢出。因此Java提供了一种垃圾收集机制,在后台创建一个守护进程。该进程会在内存紧张的时候自动跳出来,把内存的垃圾全部进行回收,从而保证程序的正常运行。

怎么判断对象是否可以被回收?

为了确定哪些对象是垃圾,有如下两种方法:引用计数法、可达性分析法。

引用计数器:给对象添加一个引用计数器,每当有一个地方引用它时,计数器的值就+1;当引用失效时,值就-1;任何时刻技术器为0的对象就是可以被回收的,它有一个缺点不能解决循环引用的问题。

可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为“引用链”。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

Java里有哪些对象可以作为GC Roots呢?

虚拟机栈(栈帧中的本地变量表)中引用的对象。

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

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

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

聊一下方法区的垃圾回收?

方法区又叫做永久代。永久代的垃圾主要有两部分:废弃常量、无用的类。

Java虚拟机规范中说,不要求虚拟机对方法区进行垃圾回收。且方法去进行垃圾回收的性价比较低。

废弃常量回收步骤:

1. 判定一个常量是否为废弃常量:没有任何一个地方对这个常量进行引用,即为废弃常量。

2. 进行回收

无用的类回收步骤:

1. 判定一个类是无用的类:①Java堆中已不存在该类的实例;②加载该类的ClassLoader已经被回收;③该类对应的Class对象在任何地方没有引用了,也不能通过反射访问该类的方法。

2. 可以进行回收了(非强制)

说下JVM有哪些垃圾回收算法?

标记-清除算法

它是最基础的收集算法,这个算法分为两个阶段,“标记”和”清除“。首先标记出所有需要回收的对象,(利用可达性遍历内存,把“垃圾”对象进行标记),在标记完成后统一回收所有被标记的对象(把所有“垃圾”对象所占空间直接清空)。

优点:简单方便

不足:

1. 效率问题,标记和清除两个过程的效率都不高;

2. 空间问题,标记清除后会产生大量不连续的内存碎片;给大对象分配内存的时候可能会提前触发full gc。

复制算法

为了解决效率问题,复制算法出现了。它可以把内存分为大小相同的两块,每次只使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块区,然后再把使用的空间一次性清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。交换两个内存的角色,完成垃圾回收。新生代98%是朝夕生死,所以按8:1:1划分,浪费10%。复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。

优点:简单、不会产生碎片

不足:内存利用率太低,浪费了一半

标记-整理算法

根据老年代的特点提出的一种标记算法,标记过程和“标记-清除”算法一样,但是后续步骤不是直接对可回收对象进行回收,而是让所有存活的对象压缩到内存的一端,然后直接清理掉边界以外的内存。

优点:不会产生内存碎片

不足:需要整理的过程,适合存活对象多,垃圾少的情况。

分代收集算法

现在的商用虚拟机的垃圾收集器基本都采用"分代收集"算法,这种算法就是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。在新生代中,每次收集都有大量对象死去,所以可以选择复制算法,只要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率时比较高的,而且没有额外的空间对它进行分配担保,就必须选择“标记-清除”或者“标记-整理”算法进行垃圾收集

首先,Eden对外提供堆内存。当Eden区快满了,触发垃圾回收机制,把存活对象放入SurvivorA,清空Eden区。

然后Eden被清空,继续对外提供堆内存。

当Eden区再次快满了,对SurvivorA、Eden区同时进行垃圾回收,把存活对象放入SurvivorB,同时清空SurvivorA、Eden区。

当某个Survivor满了,把多余对象放到Old区。

当Old区快满了,进行下一阶段垃圾回收。

老年代存活对象多、垃圾少。因此可以直接标记-整理即可。

说下JVM有哪些垃圾回收器?

Java虚拟机规范对垃圾收集器应该如何实现没有任何规定,因为没有所谓最好的垃圾收集器出现,更不会有万金油垃圾收集器,每个回收器都是需要存在的,只能是根据具体的应用场景选择合适的垃圾收集器。

像单核的CPU,或者客户端的那种程序(需要的内存很小),其实用串行回收器(Serial)是很优的选择;对吞吐量(用户线程执行总时间/用户线程总时间+垃圾回收总时间)要求很严格的场景,使用Parallel 是更优的选择;G1是优先回收性价比(回收时间/回收能释放多少空间)最高的区域。是机器CPU核心数越来越多,大型java应用的最优选择。

除G1,其他的垃圾回收器都是需要分代的,像CMS只能回收老年代,需要和ParNew 或者 Serial 能回收新生代的回收器配合。

CMS是标记清除算法(因为需要并发清除,用户线程还在运行,没办法用 标记整理)所以导致CMS碎片很多。太多的时候会来一个Serial Old进行FullGc

其实GC回收在寻找哪些对象能回收(标记阶段)是最耗时的。回收阶段其实基本不怎么耗时。

所以CMS与G1说的是可以并发搞事情,其实只是在某些耗时的地方并发了。都没有真正的并发。

Serial收集器

Serial(串行)收集器是最基本、历史最悠久的垃圾收集器。单线程收集器。它的“单线程”的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。对于运行在Client模式下的虚拟机来说是个不错的选择。停顿时间<100ms。

新生代收集器,采用复制算法。

优点:简单而高效(与其他收集器的单线程相比);由于没有线程交互的开销,在单CPU环境下,清除效率较高。

Serial Old收集器

Serial收集器的老年代版本,单线程收集器。回收时会Stop The World。它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用达到较好的吞吐量,另一种用途是作为CMS收集器的后备方案。

老年代收集器,采用标记-整理算法。

ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本。回收时会Stop The World。除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器完全一样。除了Serial收集器外,只有它能与CMS收集器配合工作。它是许多运行在Server模式下的虚拟机的首要选择。

新生代收集器,采用复制算法。

CMS收集器

并行和并发概念补充:

串行(Parallel) :垃圾回收线程进行垃圾回收工作,但此时用户线程处于等待状态。并行(Parallel) :指用户线程和多条垃圾收集线程。分别在不同的CPU上同时工作。并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个CPU上。

 

CMS收集器是HotSpot虚拟机第一款真正意义上的并发收集器。多线程收集器。是一种以获取最短回收停顿时间为目标的收集器。它而非常符合在注重用户体验的应用上使用。它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

老年代收集器,“标记-清除”算法。

缺点:对CPU资源敏感(CMS默认启动的回收线程数是(cpu数量+3)/ 4,所以CPU数量少会导致用户程序执行速度降低较多。);无法处理浮动垃圾(清除时用户进程同时产生的垃圾);产生大量空间碎片

优点:并发收集、低停顿。

Parallel Scavenge收集器

Parallel Scavenge 收集器类似于ParNew 收集器。ParNew回收器通过控制垃圾回收的线程数来进行参数调整。回收时会Stop The World。Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。 Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,手工优化存在的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

新生代收集器,采用复制算法。

Parallel Old收集器

Parallel Scavenge收集器的老年代版本。使用多线程。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器。

 老年代收集器,采用标记-整理算法。

G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,JDK1.7正式投入使用,用于取代CMS。打散Eden、S区。主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。

G1 从整体是标记整理,小区域属于标记复制。

优点:

并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行

分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。

空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的

可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1 和 CMS 共同的关注点,但G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内

 

在标记的方式上和CMS一样可以并发标记,但是清除时候其实是暂停了用户线程的。

G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了GF收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。

详细介绍下CMS以及G1垃圾回收器?

CMS(Concurrent Mark Swep)收集器是一个比较重要的回收器,现在应用非常广泛。是一种获取最短回收停顿时间为目标的收集器,这使得它很适合用于和用户交互的业务。

基于标记清除算法实现的。

它的收集过程分为四个步骤:

初始标记(CMS initial mark):标记与GC Root直接相连的对象,暂停所有的其他线程Stop The World,速度很快

并发标记(CMS concurrent mark):GCRootsTracing,从并发标记中的root遍历,对不可达的对象进行标记。同时开启GC和用户线程,用时较长。因为用户线程可能会不断的更新引用域,所以GC线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方

重新标记(CMS remark):Stop The World,修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短

并发清除(CMS concurrent sweep): 开启用户线程,同时GC线程开始对为标记的区域做清扫。

注意初始标记和重新标记还是会stop the world,但是在耗费时间更长的并发标记和并发清除两个阶段都可以和用户进程同时工作。

G1被视为JDK1.7中HotSpot虚拟机的一个重要进化特征。高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。

G1 从整体是标记整理,小区域属于标记复制

G1收集器的运作大致分为以下几个步骤:

初始标记(Initial Mark):标记GC root能直接关联的对象(短暂STW)

并发标记(Concurrent mark): GCRootsTracing,从并发标记中的root遍历,对不可达的对象进行标记,耗时长但可并行

最终标记(Final Remark):收集并发标记期间产生的新垃圾(短暂STW),采用了SATB算法比CMS更快

筛选回收(Live Data Counting and Evacuation):对各个Region的回收性价比排序,在保证时间可控的情况下清除失活对象,清除Remember Sets

CMS是并发的,非并行的,为了解决因标记清除算法而会产生内存碎片的问题,提供了-XX:UseCMSCompactAtFullCollection开发参数用于开启内存碎片的合并整理,由于内存整理是无法并行的,所以停顿时间会变长。

为什么CMS会产生浮动垃圾,而G1不会产 生浮动垃圾?

由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然会有新垃圾产生,这部分垃圾得标记过程之后,所以CMS无法在当收集中处理掉他们,只好留待下一次GC清理掉,这一部分垃圾称为浮动垃圾。

g1也同样会遇到浮动垃圾的问题,但相比于cms,g1采用了stab算法来解决浮动垃圾。

stap全称snapshot-at-the-beginning,由Taiichi Yuasa为增量式标记清除垃圾收集器开发的一个算法,主要有以下几个特点:

stab算法主要应用于垃圾收集的并发标记阶段

stab算法保证了在并发标记过程中新分配对象不会漏标

解决了CMS垃圾收集器重新标记阶段长时间STW的潜在风险

怎么选择垃圾收集器?

1. 优先调整堆的大小让服务器自己来选择

2. 如果内存小于100m,使用串行收集器

3. 如果是单核,并且没有停顿时间的要求,串行或JVM自己选择

4. 如果允许停顿时间超过1秒,选择并行或者JVM自己选

5. 如果响应时间最重要,并且不能超过1秒,使用并发收集器

官方推荐G1,性能高。

标签:JRE,JDK,收集器,标记,内存,回收,垃圾,JVM,加载
From: https://www.cnblogs.com/plusjerry/p/16995423.html

相关文章

  • jvm垃圾回收
    jvm垃圾回收第二篇......
  • 一文带你弄懂 JVM 三色标记算法!
    大家好,我是树哥。最近和一个朋友聊天,他问了我JVM的三色标记算法。我脑袋一愣发现竟然完全不知道!于是我带着疑问去网上看了几天的资料,终于搞清楚啥事三色标记算法,它是用来......
  • 一步步优化JVM五:优化延迟或者响应时间
    本节的目标是做一些优化以满足对应用对延迟的需求。这次需要几个步骤,包括完善Java堆大小的配置,评估垃圾回收占用的时间和频率,也许还要尝试切换到不同的垃圾回收器,以及由于使......
  • java JVM和class字节码版本对照表
    链接前往JDKVersionBytecodeVersionJava1.045.0Java1.145.3Java1.246.0Java1.347.0Java1.448.0Java549.0Java650.0Java7......
  • 新项目决定用 JDK 17了
    大家好,我是风筝,公众号「古时的风筝」,专注于Java技术及周边生态。文章会收录在JavaNewBee中,更有Java后端知识图谱,从小白到大牛要走的路都在里面。最近在调研JDK......
  • Spring Loaded is a JVM agent for reloading class file changes
     <plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><dependenc......
  • Address already in use: JVM_Bind 端口被占用的几个解决办法
    运行Tomcat时若出现Addressalreadyinuse:JVM_Bind端口被占用,一般使用下面几个办法可以解决:假设端口为10991.启动cmd,执行命令netstat-ano|findstr1099,会查......
  • ConcurrentHashMap完全解析(jdk6/7,8)
    并发编程实践中,ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能......
  • 010_JVM基础
    目录JVM探究JVM知识点JVM的位置JVM的体系结构类加载器双亲委派机制沙箱安全机制沙箱的基本组件NativePC寄存器方法区栈数据结构栈内存三种JVM堆新生区老年区永久区堆内存调......
  • 使用Ratpack与Spring Boot构建高性能JVM微服务
    在微服务天堂中Ratpack和SpringBoot是天造地设的一对。它们都是以开发者为中心的运行于JVM之上的web框架,侧重于生产率、效率以及轻量级部署。他们在服务程序的开发中带来了......