6. GC收集器有哪些?它们的特点是?

常见的GC收集器如下图所示,连线代表可搭配使用:

1.Serial收集器(串行收集器)

用于新生代的单线程收集器,收集时需要暂停所有工作线程(Stop the world)。优点在于:简单高效,单个CPU时没有线程交互的开销,堆较小时停顿时间不长。常与Serial Old 收集器一起使用,示意图如下所示:

2.ParNew收集器(parallel new 收集器,新生代并行收集器)

Serial收集器多线程版本,除了使用多线程外和Serial收集器一模一样。常与Serial Old 收集器一起使用,示意图如下:
 

3.Parallel Scavenge收集器

与ParNew收集器一样是一款多线程收集器,其特点在于关注点与别的GC收集器不同:一般的GC收集器关注于缩短工作线程暂停的时间,而该收集器关注于吞吐量,因此也被称为吞吐量优先收集器。(吞吐量 = 用户运行代码时间 /  (用户运行代码时间 + 垃圾回收时间))高吞吐量与停顿时间短相比主要强调任务快完成,因此常和Parallel Old 收集器一起使用(没有Parallel Old之前与Serial Old一起使用),示意图如下:
 

4.Serial Old收集器

Serial收集器的年老代版本,不在赘述。
 

5.Parallel Old收集器

年老代的并行收集器,在JDK1.6开始使用。
 

6.CMS收集器(Concurrent Mark Sweep,并发标记清除收集器)

CMS收集器是一个年老代的收集器,是以最短回收停顿时间为目标的收集器,其示意图如下所示:
CMS收集器基于标记清除算法实现,主要分为4个步骤:
  1. 初始标记,需要stop the world,标记GC Root能关联到的对象,速度快
  2. 并发标记,对GC Root执行可达性算法
  3. 重新标记,需要stop the world,修复并发标记时因用户线程运行而产生的标记变化,所需时间比初始标记长,但远比并发标记短
  4. 并发清理
CMS收集器的缺点在于:
  1. 其对于CPU资源很敏感。在并发阶段,虽然CMS收集器不会暂停用户线程,但是会因为占用了一部分CPU资源而导致应用程序变慢,总吞吐量降低。其默认启动的回收线程数是(cpu数量+3)/4,当cpu数较少的时候,会分掉大部分的cpu去执行收集器线程
  2. 无法处理浮动垃圾,浮动垃圾即在并发清除阶段因为是并发执行,还会产生垃圾,这一部分垃圾即为浮动垃圾,要等下次收集
  3. CMS收集器使用的是标记清除算法,GC后会产生碎片

7.G1收集器(Garbage First收集器)

相比CMS收集器,G1收集器主要有两处改进:
  1. 使用标记整理算法,确保GC后不会产生内存碎片
  2. 可以精确控制停顿,允许指定消耗在垃圾回收上的时间
G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是由于它能够极力地避免全区域的垃圾收集,之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的来由)。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获得最高的收集效率。
 
 

7. Minor GC与Full GC分别在什么时候发生?

Minor GC也叫Young GC,当年轻代内存满的时候会触发,会对年轻代进行GC
Full GC也叫Major GC,当年老代满的时候会触发,当我们调用System.gc时也可能会触发,会对年轻代和年老代进行GC
 

8. 类加载的五个过程:加载、验证、准备、解析、初始化

JVM把class文件加载的内存,并对数据进行校验、转换解析和初始化,最终形成JVM可以直接使用的Java类型的过程就是加载机制。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称链接。

1.加载

在加载阶段,虚拟机需要完成以下事情:
  1. 通过一个类的权限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在java堆中生成一个代表这个类的java.lang.Class对象,作为方法去这些数据的访问入口

2.验证

在验证阶段,虚拟机主要完成:
  1. 文件格式验证:验证class文件格式规范
  2. 元数据验证:这个阶段是对字节码描述的信息进行语义分析,以保证起描述的信息符合java语言规范要求
  3. 字节码验证:进行数据流和控制流分析,这个阶段对类的方法体进行校验分析,这个阶段的任务是保证被校验类的方法在运行时不会做出危害虚拟机安全的行为
  4. 符号引用验证:符号引用中通过字符串描述的全限定名是否能找到对应的类、符号引用类中的类,字段和方法的访问性(private、protected、public、default)是否可被当前类访问

3.准备

准备阶段是正式为类变量(被static修饰的变量)分配内存并设置变量初始值(0值)的阶段,这些内存都将在方法区中进行分配
 

4.解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
常见的解析有四种:
  1. 类或接口的解析
  2. 字段解析
  3. 类方法解析
  4. 接口方法解析
 

5.初始化

初始化阶段才真正开始执行类中定义的java程序代码,初始化阶段是执行类构造器<clinit>()方法的过程
 

9. 双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader

JVM的类加载是通过类加载器实现的, Java中的类加载器体系结构如下:
  • 启动类加载器(Bootstrap ClassLoader):是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
  • 标准扩展类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器
  • 应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径classpath上所指定的类库,是类加载器ClassLoader中的getSystemClassLoader()方法的返回值,开发者可以直接使用应用程序类加载器,如果程序中没有自定义过类加载器,该加载器就是程序中默认的类加载器
值得注意的是:上述三个JDK提供的类加载器虽然是父子类加载器关系,但是没有使用继承,而是使用了组合关系。

从JDK1.2开始,JVM规范推荐开发者使用双亲委派模式(ParentsDelegation Model)进行类加载,其加载过程如下:
  1. 如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成
  2. 每一层的类加载器都把类加载请求委派给父类加载器,直到所有的类加载请求都传递给顶层的启动类加载器
  3. 如果顶层的启动类加载器无法完成加载请求,则子类加载器会尝试去加载,如果连最初发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载
采用双亲委派模型的好处在于:使得java类随着它的类加载器一起具备了一种带有优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。
 

10. 分派:静态分派与动态分派

要理解分派,我们先来理解Java中的两种类型:
  • 静态类型:变量声明时的类型
  • 实际类型:变量实例化时采用的类型
举例的Java代码如下:
  1. class Car {}
  2. class Bus extends Car {}
  3. public class Main {
  4. public static void main(String[] args) throws Exception {
  5. // Car 为静态类型,Bus 为实际类型
  6. Car car = new Bus();
  7. }
  8. }

静态分派

所有依赖静态类型来定位方法执行版本的分派动作称为静态分派,其典型应用是方法重载(重载是通过参数的静态类型而不是实际类型来选择重载的版本的)。
举例Java代码如下:
  1. class Car {}
  2. class Bus extends Car {}
  3. class Jeep extends Car {}
  4. public class Main {
  5. public static void main(String[] args) throws Exception {
  6. // Car 为静态类型,Car 为实际类型
  7. Car car1 = new Car();
  8. // Car 为静态类型,Bus 为实际类型
  9. Car car2 = new Bus();
  10. // Car 为静态类型,Jeep 为实际类型
  11. Car car3 = new Jeep();
  12. showCar(car1);
  13. showCar(car2);
  14. showCar(car3);
  15. }
  16. private static void showCar(Car car) {
  17. System.out.println("I have a Car !");
  18. }
  19. private static void showCar(Bus bus) {
  20. System.out.println("I have a Bus !");
  21. }
  22. private static void showCar(Jeep jeep) {
  23. System.out.println("I have a Jeep !");
  24. }
  25. }

代码输出如下:


从上面的例子我们可以看出重载调用的具体方法版本是由静态类型来决定的。
 

动态分派

与静态分派类似,动态分派指在在运行期根据实际类型确定方法执行版本,其典型应用是方法重写(即多态)。
举例Java代码如下:
  1. class Car {
  2. public void showCar() {
  3. System.out.println("I have a Car !");
  4. }
  5. }
  6. class Bus extends Car {
  7. public void showCar() {
  8. System.out.println("I have a Bus !");
  9. }
  10. }
  11. class Jeep extends Car {
  12. public void showCar() {
  13. System.out.println("I have a Jeep !");
  14. }
  15. }
  16. public class Main {
  17. public static void main(String[] args) throws Exception {
  18. // Car 为静态类型,Car 为实际类型
  19. Car car1 = new Car();
  20. // Car 为静态类型,Bus 为实际类型
  21. Car car2 = new Bus();
  22. // Car 为静态类型,Jeep 为实际类型
  23. Car car3 = new Jeep();
  24. car1.showCar();
  25. car2.showCar();
  26. car3.showCar();
  27. }
  28. }

运行结果如下:


可以看出来重写是一个根据实际类型决定方法版本的动态分派过程。

JVM常见问题(二)的更多相关文章

  1. Java虚拟机详解----JVM常见问题总结

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  2. JVM(二)Java虚拟机组成详解

    导读:详细而深入的总结,是对知识"豁然开朗"之后的"刻骨铭心",想忘记都难. Java虚拟机(Java Virtual Machine)下文简称jvm,上一篇我 ...

  3. JVM总括二-垃圾回收:GC Roots、回收算法、回收器

    JVM总括二-垃圾回收:GC Roots.回收算法.回收器 目录:JVM总括:目录 一.判断对象是否存活 为了判断对象是否存活引入GC Roots,如果一个对象与GC Roots没有直接或间接的引用关 ...

  4. JVM(十二):方法调用

    JVM(十二):方法调用 在 JVM(七):JVM内存结构 中,我们说到了方法执行在何种内存结构上执行:Java 方法活动在虚拟机栈中的栈帧上,栈帧的具体结构在内存结构中已经详细讲解过了,下面就让我们 ...

  5. 深入JVM(二)JVM概述

    深入JVM(一)JVM指令手册 深入JVM(二)JVM概述 一.JVM的原理 Java虚拟机是Java平台的基石,解决了硬件和操作系统的相互独立性.不同平台(Windows,Linux和MacOS)的 ...

  6. JVM常见问题分析

    JVM常见问题分析 启动,并且去查看日志 ./startup.sh && tail -f ../logs/catalina.out 常见有有以下几个问题: 1.java.lang.Ou ...

  7. JVM系列二:GC策略&内存申请、对象衰老

    JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...

  8. JVM的生命周期——JVM之二

    一.首先分析两个概念 JVM实例和JVM执行引擎实例 (1)JVM实例对应了一个独立运行的java程序——进程级别 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程 ...

  9. jvm系列 (二) ---垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 前言:本文基于<深入java虚拟机>再加上个人的理解以及其他相关资料,对内容进行整理浓缩总结.本文中的图来自网络,感谢图的作者.如果有不正确的地方,欢迎指出. 目 ...

随机推荐

  1. Java 10 的 10 个新特性,将彻底改变你写代码的方式!

    Java 9才发布几个月,很多玩意都没整明白,现在Java 10又快要来了.. 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JDK 9 才发布不久不知道啥玩意,JDK ...

  2. IDEA里面创建maven项目,依赖

    在IDEA里面创建一个简单的Maven项目: 在file-->new-->project ,选择maven,点击next 里面的一些简单参数的定义(第一次使用的话可以使用默认的值进行后面的 ...

  3. js常用的数组方法

    1.创建数组的基本方法:  1.1 空数组  var obj=new Array();                 1.2 指定长度数组  var obj=new Array(size);     ...

  4. HRBUST1522【单调队列+DP】

    题目:输入一个长度为n的整数序列(A1,A2,--,An),从中找出一段连续的长度不超过m的子序列,使得这个子序列的和最大. #include<stdio.h> #include<s ...

  5. java 中String类的常用方法总结,带你玩转String类。

    String类: String类在java.lang包中,java使用String类创建一个字符串变量,字符串变量属于对象.String类对象创建后不能修改,StringBuffer & St ...

  6. python 进程 线程

    进程 线程 操作系统 为什么要有操作系统? 操作系统:操作系统是一个用来协调,管理和控制计算机硬件和软件资源的系统程序.位于底层硬件与应用软件之间 工作方式:向下管理硬件 向上提供接口 切换 1.出现 ...

  7. 前端学习之jquery

    前端学习之jquery 1.   什么是jQuery对象? jQuery对象就是通过jQuery包装DOM对象后产生的对象.jQuery对象是jQuery独有的.如果一个对象是jQuery对象,那么它 ...

  8. POJ-1679 The Unique MST---判断最小生成树是否唯一

    题目链接: https://vjudge.net/problem/POJ-1679 题目大意: 给定一个无向连通网,判断最小生成树是否唯一. 思路: (1)对图中的每条边,扫描其他边,如果存在相同权值 ...

  9. CSS Box Model 盒子模型

    1. 介绍 1.1 什么是 Box Model 在HTML中的每个element(元素)都可以看作一个矩形的盒子,矩形从内到外依次由元素的内容(content).内边距(padding).边框(bor ...

  10. CNN中减少网络的参数的三个思想

    CNN中减少网络的参数的三个思想: 1) 局部连接(Local Connectivity) 2) 权值共享(Shared Weights) 3) 池化(Pooling) 局部连接 局部连接是相对于全连 ...