文章目录
- 一、JVM概述
- 1.简述JVM,JRE,JDK
- 2.JVM,JRE,JDK的联系
- 3.JVM的特点
- 4.JVM的组成
- 5.JVM的组成的用途
- 二、类加载
- 1.类加载子系统的作用
- 2.类加载Class Loader的角色
- 3.类加载的过程
- 4.类加载器分类
- 5.双亲委派机制
- 6.沙箱安全机制
- 7.类的主动使用/被动使用
- 三、JVM运行时数据区
- 1.运行时数据区组成
- 2.程序计数器
- 3.Java 虚拟机栈
- 4.本地方法栈
- 5.Java 堆内存
- 6.堆中对象创建内存分配过程
- 7.分代收集思想 Minor GC、Major GC、Full GC
- 8.TLAB机制
- 9.字符串常量池为什么需要调整位置?
- 10.方法区的基本理解
- 11.方法区的垃圾回收
- 四、本地方法接口
- 1.什么是本地方法
- 2.为什么要使用本地方法
- 五、执行引擎
- 1.概述
- 2.什么是解释器?什么是JIT编译器
- 3.为什么java是半编译半解释型语言
- 4.JIT 编译器执行效率高为什么还需要解释器?
- 六、垃圾回收
- 1.概述
- 2.什么是垃圾
- 3.为什么需要GC
- 4.Java 垃圾回收机制
- 5.垃圾回收算法
- 6.可达性算法
- 7.标记清除算法
- 8.复制算法
- 9.标记-压缩算法
- 10.分代收集算法
- 11.增量收集算法
- 七、垃圾回收补充
- 1.System.gc()
- 2.内存溢出和内存泄漏
- 3.STW(Stop the World )
- 4.对象的引用
- 5.垃圾回收器分类
- 6.垃圾回收器
一、JVM概述
1.简述JVM,JRE,JDK
- JVM:
- java虚拟机,java运行的环境,是一个虚构出来的计算机,是通过实际的计算机模拟仿真各个功能的来实现的,
- 对于java用户来说,他可以运行.class文件,
- java虚拟机在执行字节码时,把字节码解释成具体平台上的机器来执行命令
- 这也就是java能够"一次编译,到处运行的原因"
- JRE:
- 包含JVM标准实现和java核心类库
- JRE是java的运行环境,并不是java的开发环境,JRE中并不包含编译器和调试器
- 我们编写的java程序必须要有JRE才能运行
- JDK:
- 整个java的核心,包括了java的运行环境,java工具(编译器等),还有java基础的类库
- 总的来说JDK是用于java程序的开发,而JRE则是只能运行class而没有编译的功能。
2.JVM,JRE,JDK的联系
- 联系
- JVM不能单独搞定.class的执行,
- 解释.class文件的时候JVM需要调用解释所需要的类库lib
- 而类库lib就存在JDK下面的JRE中,JRE共俩个目录,一个是bin一个是lib,此bin就可以理解为JVM
- 总的来说,我们利用JDK开发后的java程序,通过JDK中的javac将我们的java文件编译成java字节码,JRE在运行这些字节码,JVM在解析这些字节码,映射在CPU中
3.JVM的特点
- 一次编译,到处运行(java语言跨平台性的主要原因)
- 自动内存管理
- 自动垃圾回收功能
4.JVM的组成
- 1.类加载区
- 2.运行时数据区
- 3.执行引擎
- 4.本地库接口
5.JVM的组成的用途
- 程序运行之前需要将java代码转换为字节码(class文件)
- JVM需要通过类加载器件文件加载在内存中的运行时数据区,
- 而字节码时jvm的一套指令规范,并不能直接交给底层操作系统去执行,所以需要特定的命令解析器执行引擎,将字节码编译成底层系统指令再交给CPU去执行,
- 而这个过程需要调用其他语言的接口本地库接口来实现整个程序的功能
二、类加载
1.类加载子系统的作用
- 类加载器子系统负责从文件系统或者网络中加载class文件,
- classLoader只负责class文件的加载,至于是否可以运行则由ExecutionEngine决定
- 加载的类信息存放于一块成为方法区的内存空间.
- 除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量
2.类加载Class Loader的角色
- class file存在于硬盘上,相当于一个模板,在执行时要加载到JVM当中,根据这个模板可以实例化n和一模一样的示例
- class file加载到JVM中,被称为DNA元数据模板,放在方法区中
- 实例化的最终成为元数据模板,这个过程需要一个运输工具(类加载器Class Loader)扮演一个快递员的角色
3.类加载的过程
- 加载:
- 通过类名(地址)获得此类的二进制字节流
- 将此字节流转换为方法区(原空间)的运行时结构
- 在内存中生成一个代表此类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
- 链接:
- 验证:
- 检验被加载的类是否有正确的内部结构,并和其他类是否协调一致
- 准备:
- 准备阶段则负责为类的静态属性分配内存,并设置默认初始值
- 解析:
- 将类的二进制数据中的符号引用替换成直接引用
- 验证:
- 初始化
- 对 static 修饰的变量或语句块进行赋值. 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
- 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
- 顺序是:父类 static –> 子类 static –> 父类构造方法- -> 子类构造方法
4.类加载器分类
- 分为引导类加载器,自定义类加载器
5.双亲委派机制
- java虚拟机对class文件采用的是按需加载的方式,
- 需要该类的时候才会将它的class文件加载到内存中生成class对象
- 而加载某个类的class文件时,java虚拟机采用的是双亲委派模式,将请求交由父亲处理
- 工作原理
- 1.如果一个类加载器收到类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行
- 2.如果父类加载器还存在其父类加载器,则会进一步向上委托,依次递归,直到最顶层启动类加载器
- 3.如果父类加载器可以完成类的加载任务,就可以成功返回,倘若服了加载器无法完成加载任务,子任务才会尝试自己去加载
- 双亲委派的优点:
- 避免类的重复加载
- 避免自己写的类动态替换java里面的核心类
- 能不能自己写个类叫java.lang.String?
- 通常不可以,但可以采取另类方法达到这个需求。
- 为了不让我们写String类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。会先找到最顶层的String类将其加载,等到我们自己写的类时,拒绝加载
- 但是,我们可以自己定义一个类加载器来避免双亲委托机制,即而实行此类
6.沙箱安全机制
- 作用:防止恶意代码污染java源代码
- 比如上面我们定义了一个类名为 String 所在包也命名为 java.lang,因为这个类本来是属于 jdk的,如果没有沙箱安全机制的话,这个类将会污染到系统中的String,
- 但是由于沙箱安全机制,所以就委托顶层的引导类加载器查找这个类,如果没有的话就委托给扩展类加载器,再没有就委托到系统类加载器.
- 但是由于String 就是 jdk 的源代码,所以在引导类加载器那里就加载到了,先找到先使用,所以就使用引导类加载器里面的 String,后面的一概不能使用,这就保证了不被恶意代码污染.
- 在 jvm 中如何判断两个对象是属于同一个类?
- 类的全类名(地址)完全一致.
- 类的加载器必须相同
7.类的主动使用/被动使用
- 主动使用:
- 通过new关键字被导致类的初始化,这是大家经常使用的初始化一个类的方式, 他肯定会导致类的加载并且初始化
- 访问类的静态变量,包括读取和更新
- 访问类的静态方法
- 对某个类进行反射操作,会导致类的初始化
- 初始化子类会导致父类的的初始化
- 执行该类的 main 函数
- 被动使用:
- 引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致初始化,比如:
- public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
- public final static int RANDOM = new Random().nextInt() ; //会导致类的初始化,主动使用
- 构造某个类的数组时不会导致该类的初始化,比如:
- Student[] students = new Student[10] ;
- 引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致初始化,比如:
- 主动使用和被动使用的区别在于类是否会被初始化.
三、JVM运行时数据区
1.运行时数据区组成
- 程序计数器
- 当前线程说执行的字节码的信号指示器
- java虚拟机栈
- java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息,每个方法从调用直至执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程
- 本地方法栈
- 与虚拟机的作用一样,只不过虚拟机栈是服务java方法的,而本地栈是为虚拟机调用Native方法服务的
- java堆
- 是java虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时候创建,java堆唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配内存
- 方法区
- 用于存储已经被虚拟机加载的类信息,常量,静态变量,即编译后代码等数据.
2.程序计数器
- 概述:
- JVM 中的程序计数寄存器命名源于CPU 的寄存器,寄存器存储指令相关的现场信息.CPU 只有把数据装载到寄存器才能运行
- 作用
- 程序计数器用来存储下一条指令的地址,与要执行的指令代码
- 由执行引擎读取下一条指令
- 为什么使用程序计数器记录当前线程的执行地址呢?
- 因为 CPU 需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪儿开始继续执行.
- JVM 的字节码解释器就需要通过改变程序计数器的值来明确下一条应该执行什么样的字节码指令.
- 程序计数器为什么被设定为线程私有的.
- 为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个程序计数器,这样一来各个线程之间便可以独立计算, 从而不相互干扰.
3.Java 虚拟机栈
- 栈和堆的区别
- 栈是运行时的单位,而堆是存储的单位
- 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据
- 堆解决的是数据存储的问题,即数据怎么放,放在哪儿
- Java 虚拟机栈是什么?
- Java 虚拟机栈,早期也叫 Java 栈.每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次方法的调用.
- Java 虚拟机栈是线程私有的.
- 生命周期和线程一致.
- 虚拟机栈的作用
- 主管 Java 程序的运行,它保存方法的局部变量(8 种基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回
- 栈的特点
- 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器.
- JVM 直接对 java 栈的操作只有两个:
- 调用方法,进栈.
- 执行结束后出栈
- 对于栈来说不存在垃圾回收问题.
- 栈中存储什么?
- 每个线程都有自己的栈,栈中的数据都以栈帧为单位存储
- 在这个线程上正在执行的每个方法都各自对应一个栈帧
- 栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息.
- 什么情况下会出现栈溢出(StackOverflowError)?
- 栈溢出就是方法执行时创建的栈帧超过了栈的深度。那么最有可能的就是方法递归调用产生这种结果。
- 通过调整栈大小,就能保证不出现溢出吗?
- 不能
- 分配的栈内存越大越好吗?
- 并不是的,只能延缓这种现象的出现,可能会影响其他内存空间
- 垃圾回收机制是否会涉及到虚拟机栈?
- 不会
4.本地方法栈
- Java 虚拟机栈管理 java 方法的调用,而本地方法栈用于管理本地方法的调用.
- 本地方法栈也是线程私有的.
- 允许被实现成固定或者是可动态扩展的内存大小.内存溢出方面也是相同的.
- 如果线程请求分配的栈容量超过本地方法栈允许的最大容量抛出StackOverflowError.
- 如果本地方法可以动态扩展,并在扩展时无法申请到足够的内存会抛出
OutOfMemoryError. - 本地方法是用 C 语言写的.
- 它的具体做法是在 Native Method Stack 中登记 native 方法,在Execution Engine 执行时加载本地方法库
5.Java 堆内存
- 一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域
- java堆区在JVM启动时的时候被创建,其空间大小也确定了,是JVM管理的最大一块内存空间
- 堆的内存大小是可以调节的
- <<java虚拟机规范>>规定,堆可以处于物理上不连续的内存空间中,但逻辑上它应该被视为连续的
- 所有的线程java堆,在这里还可以划分线程私有的缓冲区
- <<java虚拟机规范>>中对java堆的描述是:所有的对象实例都应当在运行时分配在堆上
- 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除
- 堆,是GC执行垃圾回收的重点区域
- 堆内存被分为新生区(新生代)+老年区(老年代)
- 新生区被分为Eden(伊甸园)和Survivoe(幸存者)区
6.堆中对象创建内存分配过程
- new的新对象先放在伊甸园
- 当伊甸园空间填满时,程序需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不在被其他对象所引用的对象进行销毁,在加载新的对象放在伊甸园区
- 然后将伊甸园区中的剩余对象移动到幸存者0区
- 如果在此触发垃圾回收,此时将上次幸存下来放到幸存者0区的对象,如果没有回收,就会被放到幸存者1区,保证一个幸存区为空
- 如果在此经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区
- 当幸存者区的对象进行了15次翻转后,将其放在养老区,
- 进入养老区后,只有当养老区的内存不足时,才会触发Major GC,进行养老区的内存清理
- 若养老区执行了 Major GC 之后发现依然无法进行对象保存,就会产生 OOM 异常.Java.lang.OutOfMemoryError:Java heap space
- 其中新生区占用内次1,老年区占2,新生代占用整个堆的1/3
7.分代收集思想 Minor GC、Major GC、Full GC
- JVM在进行GC时,并非每次都和新生区和老年区一起回收,大部分时候回收的都是新生区.针对HotSpot VM的实现,他会分成俩大类型,一种是部分收集,一种是整堆收集
- 部分收集:
- 不是完整的收集
- 新生区收集:只是新生区的垃圾收集
- 老年区收集:只是老年区的垃圾收集
- 混合收集:收集整个新生区以及部分老年区的垃圾
- 不是完整的收集
- 整堆收集:
- System.gc();
- 老年区空间不足
- 方法区空间不足
- 开发期间尽量避免整堆收集
8.TLAB机制
- 为什么有TLAB
- 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据
- 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全
- 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度
- 什么是TLAB
- 线程本地分配缓存区,这是一个线程专用的内存分配区域
- 在线程初始化的同时,线程也会自动申请一块指定大小的内存,这样每个线程都会独立的拥有衣蛾空间,如果需要分配内存,就是在自己的空间上分配,这样就不存在竞争的情况,可以大大的提升分配效率
- JVM使用TLAB来避免多线程冲突,在分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高对象分配的效率,
- TLAB空间的内存非常小,只占Eden空间的1%,也可以通过选项来设置TLAB空间所占Eden空间的百分比
9.字符串常量池为什么需要调整位置?
- JDK7将字符串常量池放到了堆空间中,
- 因为永久代的回收效率很低,在Full GC的时候才会执行永久代的垃圾回收,而Full GC是老年代的空间不足,永久代不足时才会触发,
- 这就导致StringTable回收效率不高,而我们开发会有大量字符串被创建,回收效率低,导致永久代内存不足,放到堆里,能及时回收内存
10.方法区的基本理解
- 方法区,是一个被线程共享的内存区域.
- 主要存储了字节码,class等元数据,static final常量,static变量,即编译器编译后的代码等数据
- 另外方法区包含了一个特殊的区域"运行时常量池"
- 方法区是一块独立于java堆的内存空间
- 方法区在JVM启动时被创建,并且他的实际物理内存空间中和java堆区一样都可以是不连续的
- 方法区的大小和堆空间一样,可以选择固定大小或者扩展
- 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出
- 方法区用于存储已被虚拟机加载的类型信息,常量,静态变量,及时编译器编译后的代码缓存,运行常量池等
11.方法区的垃圾回收
- 运行时常量池中废弃的常量和不在使用的类型
- 卸载
- 判定一个类是否不再被使用,条件比较苛刻,
- 该类的所有实例都已被回收
- java堆中不存在该类及其任何派生子类的实例
- 加载类的类加载以及被回收,
- 该类对应的java.lang,Class对应没有任何地方被引用,无法在任何地方通过反射访问该类的方法
- 判定一个类是否不再被使用,条件比较苛刻,
四、本地方法接口
1.什么是本地方法
- Native Method就是一个java调用非java代码的接口
- 他是由非java语言实现.
2.为什么要使用本地方法
- 与java环境外交互:
- java需要一些底层系统,比如某些硬件交换信息时的情况,
- 本地方法就为我们提供这样一个交流机制
- 他为我们提供了一个简洁的接口,使我们无需区了解java之外的繁琐细节
- 与操作系统交互
- JVM支持java语言本身和运行库,是一个java程序依赖的平台,他由一个解释器和一些连接到本地代码的库组成,
- 不管如何,他还是一个不完整的系统,还需要一些底层系统的直吹,这些底层系统,常常是强大的操作系统
五、执行引擎
1.概述
- 执行引擎是java虚拟机核心组成部分之一
- JVM的主要任务是负责装在字节码到其内部,
- 但是字节码并不能够直接运行在操作系统之上,因为字节码指令并不是本地机器指令
- 所以,如果想让一个java程序运行起来,执行引擎的任务就是将字节码指令解释或编译成对应平台上的本地机器指令才可以.
- 简单来说,jvm中的执行引擎充当了高级语言翻译为机器语言的译者
- 注:
- 前端编译:从 Java 程序员-字节码文件的这个过程叫前端编译.
- 执行引擎这里有俩种行为:一种是解释执行,一种是编译执行(这里是后端编译)
2.什么是解释器?什么是JIT编译器
- 解释器:
- 当java虚拟机启动时会根据预定义字节码采用逐行解释的方式执行,将每条字节码文件的内容"翻译"为对应平台的本地机器指令执行
- JIT编译器:
- 就是虚拟机将源代码一次性直接编译成本地机器平台相关的机器语言,但不是马上执行
3.为什么java是半编译半解释型语言
- 在虚拟机中存在JIT,即时编译器,
- 它能够捕获程序中的热点代码,然后编译成机器码缓存在方法区中,
- 当遇见相同的代码时,可以避免解释器重复多次的解释执行
4.JIT 编译器执行效率高为什么还需要解释器?
- 当程序启动后,解释器可以马上发挥作用,响应速度快,省去编译的时间,立即执行
- 编译器想要发挥作用,把代码编译成本地代码,需要一定的执行时间,但编译成本地代码后,执行效率高.
- 所以需要采用解释器与即时编译器并存的架构来换取一个平衡点
六、垃圾回收
1.概述
- java和C++的语言区别就是
- 垃圾收集技术和内存动态分配上
- C++没有垃圾收集技术,需要程序员手动的收集
- 垃圾收集机制是java的招牌能力,极大地提高了开发效率,
2.什么是垃圾
- 垃圾是指在运行程序中没有任何指针指向的对象
- 如果不及时对内存中的垃圾进行清理,那么这些对象所占用的空间会一直保留到应用程序结束,就会导致内存溢出
3.为什么需要GC
- 对于高级语言来说,如果不进行垃圾回收,内存迟早会被消耗完,
- 除了释放没用的对象,垃圾回收也会清理内存里面的记录碎片,以便JVM将整理出的内存分配给新的对象
4.Java 垃圾回收机制
- 自动内存管理
- 无需内存管理,无需开发人员手动参与内存的分配与回收,可以降低内存泄漏和内存溢出的风险
- 可以将程序员从繁琐的内存管理中释放出来,可以更专心地专注于业务开发
- 自动内存管理的担忧
- 会弱化java开发人员在程序出现内存溢出时定位问题和解决问题的能力
- 哪些区域会被回收
- 新生代,老年代,全栈,方法区
5.垃圾回收算法
标记清除,复制,标记压缩
6.可达性算法
- 对象可达:
- 双方存在直接或间接的引用关系
- 根可达就是对象到GC Roots存在直接或者间接的引用关系
7.标记清除算法
- 标记:从根节点开始遍历,标记所以被引用的对象.即标记可带对象
- 清除:有一个集合,记录垃圾位置,有新对象时,覆盖垃圾位置
- 优点: 简单
- 缺点:会产生内存碎片
8.复制算法
- 有俩块内存区域,一块是空闲的
- 垃圾回收时,把没有回收的对象,复制在空闲内存区域,即幸存者1到幸存者2
- 优点:对于少量对象,运行快,不会产生碎片
- 缺点:内存需要2块,对象需要发生复制,大量对象效率低
9.标记-压缩算法
- 标记:先标记可达对象
- 压缩:将存活的对象整理到内存一端,按照顺序排放
- 优点:不会产生内存碎片
- 缺点:效率低
10.分代收集算法
- 不同的区域使用不同的垃圾回收算法
- 新生代:幸存者1 幸存者2 复制算法
- 老年代:标记压缩和标记清除混合使用
11.增量收集算法
- 如果一次性将所有垃圾进行处理,需要系统长时间的停顿,就是为了让垃圾收集算法间断进行.
- 垃圾回收时,用户线程需要停止(STW)
- 优点:每次回收部分垃圾,回收线程和用户线程交替执行,减少用户线程停顿时间
- 缺点:切换次数开销增大,每次回收垃圾少
七、垃圾回收补充
1.System.gc()
- 显示的调用gc();方法,不一定立即执行垃圾回收
- java中的垃圾回收是自动的,垃圾回收也是一个线程,需要CPU加载
2.内存溢出和内存泄漏
- 内存溢出:堆,栈,方法区
- 内存泄漏:IO close()数据库连接close()有些对象以及不会使用了,但是垃圾回收缺不能将其清除,会一直占用内存空间
3.STW(Stop the World )
- 增量收集算法
- GC 事件发生过程中,会产生应用程序的停顿
- 垃圾回收标记算法,需要对某一时间节点的内存的对象进行分析,判断他是否是一直变化的,来定位他是不是垃圾
- 所以STW是所有垃圾回收器都不可避免的
4.对象的引用
- 强引用
- String s = new String(“aaaaa”);
- s.length();
- 强引用,百分之99的引用都是前饮用,是不会被回收的,报OOM
- 软引用
- 使用 SoftReference进行标记,内存够时,只要可达就不会被回收,在内存不够的时候也会被回收
- 对软引用对象回收后,内存依然不够使用,报OOM
- 弱引用
- 使用Weak Reference标记,发现即回收,不论内存是否够用。
- 虚引用:
- 使用Phantom Reference标记,相当于没有引用随时都会被回收
5.垃圾回收器分类
- 回收线程数量:
- 串行:只要一个垃圾回收线程
- 并行:有多个那就回收线程
- 按照工作模式:
- 分类并发式垃圾回收器和独占式垃圾回收器
- 按照工作的内存区间分:
- 又可分为年轻代垃圾回收器和老年代垃圾回收器
- 性能指标
- 吞吐量: 程序运行时间/总时间(程序运行时间+垃圾回收时间)
- 垃圾收集开销:垃圾收集所用时间与总运行时间的比例
- 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。
- 收集频率:相对于应用程序的执行,收集操作发生的频率。
- 内存占用:Java 堆区所占的内存大小。
- 快速:一个对象从诞生到被回收所经历的时间。
6.垃圾回收器
- 新生代收集器:Serial,ParNew,Parallel ,scavenge;
- 老年代收集器:Serial old.Parallel old.CMS;
- 整堆收集器:G1;(现在的虚拟机使用)
- G1 分区垃圾收集
- 保证低延迟,提高吞吐量
- 将堆的每个区域进行细分,分成多个区域, 回收时可以避免整堆回收,分区域进行.
- CMS 回收器**(低延迟)**
- 优点:追求低停顿,用户线程和垃圾回收线程并发执行
- 缺点:产生碎片,并发时占用用户线程,
更多推荐
JVM的一键入脑
发布评论